import request from 'utils/request'; import config from 'config/Config' /** * 文件上传 * @param {*} url 获取签名url * @param {*} file 上传文件 * @param {*} callback 回调函数 * @param {*} otherParams 其他参数 * @param {*} type 上传类型 oss s3 huawei aws */ let uploadFn = (url = '', file = {}, callback, otherParams = {}, type = 'oss') => { url += `?fileType=${file.type}`; // bucketKey 云配置的桶,相当于命名空间 config.bucketKey && (url += `&&bucketKey=${config.bucketKey}`); let params = { ossType: getUploadType(type), url } getSgin(params).then(res => { if (`${res.resultCode}` === '0') { let fnMap = { huawei: huaweiUpload, s3: s3Upload, aws: awsUpload, oss: ossUpload, } fnMap[type](file, callback, otherParams, res.data) } }) } // 获取上传类型 let getUploadType = (type) => { let typeEnum = { s3: 'MINIO', oss: 'ALIYUNOSS', huawei: 'HUAWEIOBS', aws: 'AMAZONS3' } return typeEnum[type] } // minio文件上传 let s3Upload = (file, callback, otherParams = {}, data = {}) => { let formData = new FormData(); let key = calculate_object_name(data.key, 'home', file.name, false); Object.keys(data).forEach(item => { if (item === 'key' || item === 'form-action') return; formData.append(item, data[item]); }) formData.append('key', `${key}${file.name}`); formData.append('file', file); beginUpload(data['form-action'], formData, otherParams, callback, file, `${key}${file.name}`); } // 亚马逊文件上传 let awsUpload = (file, callback, otherParams = {}, data = {}) => { let formData = new FormData(); let key = calculate_object_name(data.key, 'home', file.name, false); Object.keys(data).forEach(item => { if (item === 'key' || item === 'form-action') return; formData.append(item, data[item]); }) formData.append('key', `${key}${file.name}`); formData.append('file', file); beginUpload(data['form-action'], formData, otherParams, callback, file, `${key}${file.name}`); } // 华为文件上传 let huaweiUpload = (file, callback, otherParams = {}, data = {}) => { let formData = new FormData(); let key = calculate_object_name(data.key, 'home', file.name, false); formData.append('x-obs-acl', data['x-obs-acl']); formData.append('content-type', file.type); formData.append('policy', data.policy); formData.append('key', key); formData.append('AccessKeyId', data.AccessKeyId); formData.append('signature', data.signature); formData.append('file', file); beginUpload(data['form-action'], formData, otherParams, callback, file, key); } // oss文件上传 let ossUpload = (file, callback, otherParams = {}, ossData = {}) => { let formData = new FormData(); let ossKey = calculate_object_name(ossData.dir, 'home', file.name, false); formData.append('OSSAccessKeyId', ossData.accessid); formData.append('policy', ossData.policy); formData.append('signature', ossData.signature); formData.append('key', ossKey); formData.append('success_action_status', 200); formData.append('expire', ossData.expire); formData.append('file', file); beginUpload(ossData.host, formData, otherParams, callback, file, ossKey); } /** * 开始上传和成功后返回函数 * @param {*} host 请求路径 * @param {*} formData 数据 * @param {*} otherParams 需要返回的参数 * @param {*} callback 回调函数 * @param {*} file 原文件 * @param {*} key 上传文件名 */ let beginUpload = (host, formData, otherParams, callback, file = {}, key) => { uploadInterface({ url: host, formData }).then(result => { // 在axios.interceptors.response中做请求失败返回,以便确定请求失败,成功是不会有返回值的 if (result) return; // 解决出现两个// host[host.length - 1] === '/' && (host = host.slice(0, -1)); otherParams._result = `${host}/${key}`; otherParams.file = file; typeof callback === 'function' && callback(otherParams); console.log(`${host}/${key}`, 'result', otherParams) return otherParams; }) } // 获取签名 let getSgin = (params = {}) => { return request({ url: params.url, method: 'GET', headers: { ossType: params.ossType } }) } // 上传文件 let uploadInterface = (params = {}) => { return request({ url: params.url, method: 'POST', data: params.formData, headers: { "Context-Type": 'multipart/form-data ' } }) } let fileSuffix = (filename) => { const splitFile = filename.split('.') const len = splitFile.length return '.' + splitFile[len - 1] } let random = (n = 32) => { var chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678' var letter = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz' var maxPos = chars.length var result = '' for (var i = 0; i < n; i++) { result += chars.charAt(Math.floor(Math.random() * maxPos)) } result = letter.charAt(Math.floor(Math.random() * letter.length)) + result return result.substring(0, result.length - 1) } // 混淆上传文件的名字 let calculate_object_name = (g_dirname, group, filename, isReal) => { if (!filename) { return '' } var suffix = fileSuffix(filename) var g_object_name = g_dirname + random(15) + suffix if (!!group && g_dirname) { if (g_dirname.indexOf('/') == -1) { g_dirname += '/' } g_object_name = decodeURIComponent(g_dirname + group + '/' + random(14) + suffix) } return g_object_name } export default uploadFn;
亚马逊完整参数
// 除了文件,其他数据由后端获取签名接口获取 x-amz-date: 20210309T080505Z x-amz-signature: 'signature' acl: 'acl' x-amz-algorithm: 'x-amz-algorithm' Content-Type: image/jpeg x-amz-credential: 'x-amz-credential' policy: 'policy' key: '混淆文件名' file: (binary)
minio完整参数
// 和亚马逊参数基本差不多 x-amz-date: 20210309T081113Z x-amz-signature: 'x-amz-signature' acl: 'acl' x-amz-algorithm: AWS4-HMAC-SHA256 Content-Type: image/jpeg x-amz-credential: 'x-amz-credential' policy: 'policy' key: '混淆文件名' file: (binary)
需要注意的一个点
当配置了xhr.withCredentials = true时,必须在后端增加 response 头信息Access-Control-Allow-Origin,且必须指定域名,而不能指定为*。会造成上传文件地址如oss跨域。
如果在同域下配置xhr.withCredentials,无论配置true还是false,效果都会相同,且会一直提供凭据信息(cookie、HTTP认证及客户端SSL证明等)
如果你需要上传base64的图片,比如截图所得到的图片就是base64的图片,可以将base64图片转化为file图片,再进行上传,转换方法如下
/** * base64图片转成file图片 * @param {*} dataurl base64图片完整路径 * @param {*} filename 即将命名的文件名 */ static base64ToFile = (dataurl, filename) => { var arr = dataurl.split(','), // 获取文件类型,如image/png mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); while (n--) { u8arr[n] = bstr.charCodeAt(n); } return new File([u8arr], filename, { type: mime }); }