import '../../common/aws-sdk-2.235.1.min.js' import { v4 as uuidv4 } from 'uuid' // 上传单个文件 const uploadOneFile = (file, progressFn) => { return new Promise((resolve) => { let credentials = { accessKeyId: 'AKIATLPEDU37QV5CHLMH', secretAccessKey: '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', } //秘钥形式的登录上传 window.AWS.config.update(credentials) window.AWS.config.region = 'cn-northwest-1' //设置区域 let bucket = new window.AWS.S3({ params: { Bucket: 'ccrb' } }) //选择桶 if (file) { var params = { Key: file.name.split('.')[0] + new Date().getTime() + '.' + file.name.split('.')[file.name.split('.').length - 1], ContentType: file.type, Body: file, 'Access-Control-Allow-Credentials': '*', ACL: 'public-read', } //key可以设置为桶的相抵路径,Body为文件, ACL最好要设置 var options = { partSize: 2048 * 1024 * 1024, queueSize: 2, leavePartsOnError: true, } bucket .upload(params, options) .on('httpUploadProgress', function (evt) { //这里可以写进度条 if (progressFn) { let _progress = { loaded:evt.loaded, total:evt.total, percent:(evt.loaded/evt.total*100).toFixed(2), } progressFn(_progress) } }) .send(function (err, data) { if (err) { resolve('') } else { let fileObj = { name: file.name, url: data.Location, size: formatFileSize(file.size), // 格式化文件大小 uid: uuidv4(), type: file.type, fileType:getFileType(file), } console.log('uploadFile', fileObj) resolve(fileObj) } }) } }) } // 获取文件数据 const getFile = (url) => { return new Promise((resolve) => { var credentials = { accessKeyId: "AKIATLPEDU37QV5CHLMH", secretAccessKey: "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", }; //秘钥形式的登录上传 window.AWS.config.update(credentials); window.AWS.config.region = "cn-northwest-1"; //设置区域 let url2 = url; let _url2 = ""; if ( url2.indexOf("https://view.officeapps.live.com/op/view.aspx?src=") != -1 ) { _url2 = url2.split( "https://view.officeapps.live.com/op/view.aspx?src=" )[1]; } else { _url2 = url2; } var s3 = new window.AWS.S3({ params: { Bucket: "ccrb" } }); let name = decodeURIComponent(_url2.split("https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/")[1]) var params = { Bucket: "ccrb", Key: name, }; s3.getObject(params, function (err, data) { if (err) { console.log(err, err.stack) resolve({ data: 1 }); } else { resolve({ data: data.Body }); console.log(data); } // sxuccessful response }); }); }; const getTxtFileContent = (url) => { return new Promise((resolve) => { var credentials = { accessKeyId: "AKIATLPEDU37QV5CHLMH", secretAccessKey: "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" }; //秘钥形式的登录上传 window.AWS.config.update(credentials); window.AWS.config.region = "cn-northwest-1"; //设置区域 let url2 = url; let _url2 = ""; if ( url2.indexOf("https://view.officeapps.live.com/op/view.aspx?src=") != -1 ) { _url2 = url2.split( "https://view.officeapps.live.com/op/view.aspx?src=" )[1]; } else { _url2 = url2; } var s3 = new window.AWS.S3({ params: { Bucket: "ccrb" } }); let name = decodeURIComponent( _url2.split("https://ccrb.s3.cn-northwest-1.amazonaws.com.cn/")[1] ); var params = { Bucket: "ccrb", Key: name }; s3.getObject(params, function (err, data) { if (err) { console.log(err, err.stack); resolve({ data: 1 }); } else { const fileContent = data.Body.toString("utf-8"); resolve({ data: fileContent }); } }); }); } const formatFileSize = (size) => { if (size < 1024) { return size + 'B' } else if (size < 1024 * 1024) { return (size / 1024).toFixed(2) + 'KB' } else if (size < 1024 * 1024 * 1024) { return (size / (1024 * 1024)).toFixed(2) + 'MB' } else if (size < 1024 * 1024 * 1024 * 1024) { return (size / (1024 * 1024 * 1024)).toFixed(2) + 'GB' } else { return (size / (1024 * 1024 * 1024 * 1024)).toFixed(2) + 'TB' } } // 常用文件类型清单 const fileTypes = [ { value: 'image', label: '图片', exts: ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'], mime: ['image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/webp', 'image/svg+xml'] }, { value: 'video', label: '视频', exts: ['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv', 'webm'], mime: ['video/mp4', 'video/x-msvideo', 'video/quicktime', 'video/x-ms-wmv', 'video/x-flv', 'video/x-matroska', 'video/webm'] }, { value: 'audio', label: '音频', exts: ['mp3', 'wav', 'aac', 'ogg', 'flac', 'm4a'], mime: ['audio/mpeg', 'audio/x-wav', 'audio/aac', 'audio/ogg', 'audio/flac', 'audio/mp4'] }, { value: 'doc', label: '文档', exts: ['doc', 'docx', 'pdf', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'md', 'csv'], mime: ['application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/pdf', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'text/plain', 'text/markdown', 'text/csv'] }, { value: 'zip', label: '压缩包', exts: ['zip', 'rar', '7z', 'tar', 'gz'], mime: ['application/zip', 'application/x-rar-compressed', 'application/x-7z-compressed', 'application/x-tar', 'application/gzip'] }, { value: 'other', label: '其它', exts: [], mime: [] } ] // 根据传入的文件类型(文件名/后缀或mime)获取value const getFileType = (file) => { if (!file) return 'other'; let ext = ''; let mime = ''; // 允许传入 string 或 File/Blob 对象 if (typeof file === 'string') { // 从文件名里提取后缀 const match = file.match(/\.([^.]+)$/); ext = match ? match[1].toLowerCase() : ''; } else if (typeof file === 'object' && file) { // File 或 Blob 情况,取type为mime,name取后缀 if (file.type) mime = file.type; if (file.name) { const match = file.name.match(/\.([^.]+)$/); ext = match ? match[1].toLowerCase() : ''; } } // 先按mime类型查找 if (mime) { for (let type of fileTypes) { if (type.mime.includes(mime)) { return type.value; } } } // 再按扩展名查找 if (ext) { for (let type of fileTypes) { if (type.exts.includes(ext)) { return type.value; } } } // 未命中则返回other return 'other'; } // 分片上传 /** * AWS S3 分片上传工具,支持进度回调及停止上传功能 * 使用方式: * const uploader = new AwsMultipartUploader(options); * uploader.upload({ file, keyName, folderName, onProgress, onSuccess, onError }); * uploader.stop(); // 随时可停止 */ class AwsMultipartUploader { constructor({ bucketname = "ccrb", accessKeyId = "AKIATLPEDU37QV5CHLMH", secretAccessKey = "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", region = "cn-northwest-1", partsize = 10 * 1024 * 1024 // 10MB } = {}) { this.bucketname = bucketname; this.accessKeyId = accessKeyId; this.secretAccessKey = secretAccessKey; this.region = region; this.partsize = partsize; this.bucket = null; this.flag = true; this.uploadid = ""; this.lastFilestate = null; // 缓存上次的filestate, 避免重复回调 } async init() { const credentials = { accessKeyId: this.accessKeyId, secretAccessKey: this.secretAccessKey, region: this.region }; window.AWS.config.update(credentials); this.bucket = new window.AWS.S3({ params: { Bucket: this.bucketname } }); } async initMultipartUpload(key, file) { const params = { Bucket: this.bucketname, Key: key, ContentType: file.type, ACL: "public-read" }; const data = await this.bucket.createMultipartUpload(params).promise(); return data.UploadId; } async uploadPart(file, keyname, uploadid, pn, start, end) { if (!this.flag) return; const params = { Bucket: this.bucketname, Key: keyname, PartNumber: pn, UploadId: uploadid, Body: file.slice(start, end) }; const result = await this.bucket.uploadPart(params).promise(); return { ETag: result.ETag, PartNumber: pn }; } async completeMultipartUpload(parts, keyname, uploadid) { if (!this.flag) return; const params = { Bucket: this.bucketname, Key: keyname, MultipartUpload: { Parts: parts }, UploadId: uploadid }; return await this.bucket.completeMultipartUpload(params).promise(); } async abortMultipartUpload(key, uploadid) { const params = { Bucket: this.bucketname, Key: key, UploadId: uploadid }; await this.bucket.abortMultipartUpload(params).promise(); } async getCheckpoint(key) { let partsinfo; let uploadid = ""; try { const result = await this.bucket .listMultipartUploads({ Bucket: this.bucketname, Prefix: key }) .promise(); if (result.Uploads && result.Uploads.length) { uploadid = result.Uploads[result.Uploads.length - 1].UploadId; partsinfo = await this.bucket .listParts({ Bucket: this.bucketname, Key: key, UploadId: uploadid }) .promise(); } } catch (err) { console.log(err); } return { uploadid, partsinfo }; } // 控制停止 stop() { this.flag = false; } emitProgress(filestate, onProgress) { // 只回调变化 if (typeof onProgress === "function") { if ( !this.lastFilestate || this.lastFilestate.percent !== filestate.percent || this.lastFilestate.status !== filestate.status ) { onProgress(Object.assign({}, filestate)); } } this.lastFilestate = Object.assign({}, filestate); } // 分段上传 async uploadParts({ file, uploadid, parts = [], key, onProgress }) { let filestate = { status: "processing", percent: 0 }; let partarr = []; const completeparts = parts.map((_) => { partarr.push(_.PartNumber); return { PartNumber: _.PartNumber, ETag: _.ETag }; }); let len = Math.ceil(file.size / this.partsize); if (partarr.length) { filestate.percent = parseInt((completeparts.length * 100) / len); this.emitProgress(filestate, onProgress); } for (let i = 0; i < len; i++) { if (!this.flag) break; let start = i * this.partsize; let end = (i + 1) * this.partsize; if (!partarr.includes(i + 1)) { const uploadpart = await this.uploadPart( file, key, uploadid, i + 1, start, end ); if (uploadpart && uploadpart.ETag != null) { completeparts.push(uploadpart); partarr.push(uploadpart.PartNumber); filestate.percent = parseInt((completeparts.length * 100) / len); filestate.status = "processing"; this.emitProgress(filestate, onProgress); } else { filestate.status = "fail"; this.emitProgress(filestate, onProgress); this.stop(); throw new Error("分片上传失败"); } } } if (this.flag) { const data = await this.completeMultipartUpload( completeparts, key, uploadid ); filestate.status = "success"; filestate.percent = 100; this.emitProgress(filestate, onProgress); return data; // 返回complete后的aws数据 } else { filestate.status = "stop"; this.emitProgress(filestate, onProgress); throw new Error("上传已中断"); } } // 主入口(异步,支持await) /** * * @param {*} param0 * file: 要上传的File对象 * keyName: 指定上传key(可选) * folderName: 文件夹(可选) * onProgress: function({status, percent}) */ async upload({ file, keyName, folderName, onProgress }) { if (!file) { throw new Error("请上传文件"); } this.flag = true; this.lastFilestate = null; await this.init(); let key = ""; if (keyName) { key = keyName; } else if (folderName) { key = `${folderName}/${file.name}`; } else { const ext = file.name.split(".").length > 1 ? file.name.substring(file.name.lastIndexOf(".") + 1) : ""; key = `${file.name.split(".")[0]}${Date.now()}${ext ? "." + ext : ""}`; } let filestate = { percent: 0, status: "start" }; this.emitProgress(filestate, onProgress); const params = { Bucket: this.bucketname, Key: key }; // 用Promise包装, 使upload支持await return new Promise(async (resolve, reject) => { try { // 检查是否已上传 this.bucket.headObject(params, async (err, data) => { if (err) { // 检查断点 let { uploadid, partsinfo } = await this.getCheckpoint(key); let curUploadId = uploadid; let awsParts = []; if (uploadid && partsinfo) { awsParts = partsinfo.Parts || []; } try { let completeResult; if (curUploadId) { completeResult = await this.uploadParts({ file, uploadid: curUploadId, parts: awsParts, key, onProgress }); } else { const newUploadId = await this.initMultipartUpload(key, file); completeResult = await this.uploadParts({ file, uploadid: newUploadId, parts: [], key, onProgress }); } let url = `https://${this.bucketname}.s3.${this.region}.amazonaws.com.cn/${key}`; const resObj = { name: file.name, url: url, size: formatFileSize(file.size), uid: uuidv4(), type: file.type, Key: key, Location: url, ETag: completeResult.ETag, Bucket: this.bucketname }; console.log(resObj) resolve(resObj); } catch (err) { console.log('分片上传失败', err) reject(''); } } else if (data) { // 已经100% let url = `https://${this.bucketname}.s3.${this.region}.amazonaws.com.cn/${key}`; const resObj = { name: file.name, url: url, size: formatFileSize(file.size), uid: uuidv4(), type: file.type, Key: key, Location: url, ETag: data.ETag, Bucket: this.bucketname }; filestate.percent = 100; filestate.status = "success"; this.emitProgress(filestate, onProgress); resolve(resObj); } }); } catch (err) { console.log('分片上传失败', err) reject(''); } }); } } export { uploadOneFile, getFileType, getFile, getTxtFileContent, AwsMultipartUploader, formatFileSize }