| @@ -1797,7 +1797,8 @@ | |||
| "assert-plus": { | |||
| "version": "1.0.0", | |||
| "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", | |||
| "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" | |||
| "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", | |||
| "optional": true | |||
| }, | |||
| "assign-symbols": { | |||
| "version": "1.0.0", | |||
| @@ -2854,6 +2855,7 @@ | |||
| "version": "1.0.8", | |||
| "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", | |||
| "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", | |||
| "optional": true, | |||
| "requires": { | |||
| "delayed-stream": "~1.0.0" | |||
| } | |||
| @@ -3590,7 +3592,8 @@ | |||
| "delayed-stream": { | |||
| "version": "1.0.0", | |||
| "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", | |||
| "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" | |||
| "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", | |||
| "optional": true | |||
| }, | |||
| "delegate": { | |||
| "version": "3.2.0", | |||
| @@ -3744,9 +3747,9 @@ | |||
| } | |||
| }, | |||
| "dropzone": { | |||
| "version": "5.7.0", | |||
| "resolved": "https://registry.npmjs.org/dropzone/-/dropzone-5.7.0.tgz", | |||
| "integrity": "sha512-kOltiZXH5cO/72I22JjE+w6BoT6uaVLfWdFMsi1PMKFkU6BZWpqRwjnsRm0o6ANGTBuZar5Piu7m/CbKqRPiYg==" | |||
| "version": "5.7.2", | |||
| "resolved": "https://registry.npm.taobao.org/dropzone/download/dropzone-5.7.2.tgz?cache=0&sync_timestamp=1596009792692&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdropzone%2Fdownload%2Fdropzone-5.7.2.tgz", | |||
| "integrity": "sha1-kb7hVy3aUV1AkB2jBLx53d8wm0w=" | |||
| }, | |||
| "duplexer2": { | |||
| "version": "0.0.2", | |||
| @@ -4806,7 +4809,8 @@ | |||
| "extsprintf": { | |||
| "version": "1.3.0", | |||
| "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", | |||
| "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" | |||
| "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", | |||
| "optional": true | |||
| }, | |||
| "fancy-log": { | |||
| "version": "1.3.3", | |||
| @@ -7621,7 +7625,8 @@ | |||
| "jsbn": { | |||
| "version": "0.1.1", | |||
| "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", | |||
| "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" | |||
| "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", | |||
| "optional": true | |||
| }, | |||
| "jsesc": { | |||
| "version": "2.5.2", | |||
| @@ -8677,12 +8682,14 @@ | |||
| "mime-db": { | |||
| "version": "1.44.0", | |||
| "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", | |||
| "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" | |||
| "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", | |||
| "optional": true | |||
| }, | |||
| "mime-types": { | |||
| "version": "2.1.27", | |||
| "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", | |||
| "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", | |||
| "optional": true, | |||
| "requires": { | |||
| "mime-db": "1.44.0" | |||
| } | |||
| @@ -13654,7 +13661,8 @@ | |||
| "tweetnacl": { | |||
| "version": "0.14.5", | |||
| "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", | |||
| "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" | |||
| "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", | |||
| "optional": true | |||
| }, | |||
| "type": { | |||
| "version": "1.2.0", | |||
| @@ -18,7 +18,7 @@ | |||
| "css-loader": "3.5.3", | |||
| "cssnano": "4.1.10", | |||
| "domino": "2.1.5", | |||
| "dropzone": "5.7.0", | |||
| "dropzone": "5.7.2", | |||
| "fast-glob": "3.2.2", | |||
| "file-loader": "6.0.0", | |||
| "fomantic-ui": "2.8.4", | |||
| @@ -1,362 +0,0 @@ | |||
| <template> | |||
| <uploader | |||
| ref="uploader" | |||
| :options="options" | |||
| :autoStart="false" | |||
| @file-added="onFileAdded" | |||
| fileStatusText="fileStatusText" | |||
| class="uploader-app"> | |||
| <uploader-unsupport></uploader-unsupport> | |||
| <uploader-drop> | |||
| <p>拖动文件</p> | |||
| <uploader-btn>选择文件</uploader-btn> | |||
| </uploader-drop> | |||
| <uploader-list></uploader-list> | |||
| <p>文件处理状态:{{status}}</p> | |||
| <p>文件上传进度:{{progress}}%</p> | |||
| </uploader> | |||
| </template> | |||
| <script> | |||
| import SparkMD5 from 'spark-md5'; | |||
| import axios from 'axios' | |||
| import qs from 'qs' | |||
| const {AppSubUrl, StaticUrlPrefix, csrf} = window.config; | |||
| export default { | |||
| data () { | |||
| return { | |||
| attrs: { | |||
| accept: '*' | |||
| }, | |||
| fileStatusText: { | |||
| success: '上传成功', | |||
| error: '上传出错了', | |||
| uploading: '上传中...', | |||
| paused: '暂停', | |||
| waiting: '等待中...', | |||
| cmd5: '计算md5...' | |||
| }, | |||
| fileStatusText: (status, response) => { | |||
| return this.fileStatusText[status]; | |||
| }, | |||
| progress: 0, | |||
| status: '初始状态', | |||
| } | |||
| }, | |||
| created() { | |||
| //const uploaderInstance = this.$refs.uploader; | |||
| }, | |||
| mounted () { | |||
| this.$nextTick(() => { | |||
| window.uploader = this.$refs.uploader.uploader | |||
| }) | |||
| }, | |||
| methods: { | |||
| onFileAdded(file) { | |||
| file.datasetId = document.getElementById("datasetId").getAttribute("datasetId"); | |||
| this.progress=0; | |||
| this.status='初始状态'; | |||
| // 计算MD5 | |||
| this.computeMD5(file); | |||
| }, | |||
| getSuccessChunks(file) { | |||
| return new Promise((resolve, reject) => { | |||
| axios.get('/attachments/get_chunks', {params :{ | |||
| md5: file.uniqueIdentifier, | |||
| _csrf: csrf | |||
| }}).then(function (response) { | |||
| file.uploadID = response.data.uploadID; | |||
| file.uuid = response.data.uuid; | |||
| file.uploaded = response.data.uploaded; | |||
| file.chunks = response.data.chunks; | |||
| file.attachID = response.data.attachID; | |||
| resolve(response); | |||
| }).catch(function (error) { | |||
| console.log(error); | |||
| reject(error); | |||
| }); | |||
| }) | |||
| }, | |||
| newMultiUpload(file) { | |||
| return new Promise((resolve, reject) => { | |||
| axios.get('/attachments/new_multipart', {params :{ | |||
| totalChunkCounts: file.totalChunkCounts, | |||
| md5: file.uniqueIdentifier, | |||
| size: file.size, | |||
| fileType: file.fileType, | |||
| _csrf: csrf | |||
| }}).then(function (response) { | |||
| file.uploadID = response.data.uploadID; | |||
| file.uuid = response.data.uuid; | |||
| resolve(response); | |||
| }).catch(function (error) { | |||
| console.log(error); | |||
| reject(error); | |||
| }); | |||
| }) | |||
| }, | |||
| multipartUpload(file) { | |||
| let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice, | |||
| chunkSize = 1024*1024*64, | |||
| chunks = Math.ceil(file.size / chunkSize), | |||
| currentChunk = 0, | |||
| fileReader = new FileReader(), | |||
| time = new Date().getTime(); | |||
| function loadNext() { | |||
| let start = currentChunk * chunkSize; | |||
| let end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize; | |||
| fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end)); | |||
| } | |||
| function checkSuccessChunks() { | |||
| var index = successChunks.indexOf((currentChunk+1).toString()) | |||
| if (index == -1) { | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| function getUploadChunkUrl(currentChunk, partSize) { | |||
| return new Promise((resolve, reject) => { | |||
| axios.get('/attachments/get_multipart_url', {params :{ | |||
| uuid: file.uuid, | |||
| uploadID: file.uploadID, | |||
| size: partSize, | |||
| chunkNumber: currentChunk+1, | |||
| _csrf: csrf | |||
| }}).then(function (response) { | |||
| urls[currentChunk] = response.data.url | |||
| resolve(response); | |||
| }).catch(function (error) { | |||
| console.log(error); | |||
| reject(error); | |||
| }); | |||
| }) | |||
| } | |||
| function uploadMinio(url, e) { | |||
| return new Promise((resolve, reject) => { | |||
| axios.put(url, e.target.result | |||
| ).then(function (res) { | |||
| etags[currentChunk] = res.headers.etag; | |||
| resolve(res); | |||
| }).catch(function (err) { | |||
| console.log(err); | |||
| reject(err); | |||
| }); | |||
| }); | |||
| } | |||
| function updateChunk(currentChunk) { | |||
| return new Promise((resolve, reject) => { | |||
| axios.post('/attachments/update_chunk', qs.stringify({ | |||
| uuid: file.uuid, | |||
| chunkNumber: currentChunk+1, | |||
| etag: etags[currentChunk], | |||
| _csrf: csrf | |||
| })).then(function (response) { | |||
| resolve(response); | |||
| }).catch(function (error) { | |||
| console.log(error); | |||
| reject(error); | |||
| }); | |||
| }) | |||
| } | |||
| async function uploadChunk(e) { | |||
| if (!checkSuccessChunks()) { | |||
| let start = currentChunk * chunkSize; | |||
| let partSize = ((start + chunkSize) >= file.size) ? file.size -start : chunkSize; | |||
| //获取分片上传url | |||
| await getUploadChunkUrl(currentChunk, partSize); | |||
| if (urls[currentChunk] != "") { | |||
| //上传到minio | |||
| await uploadMinio(urls[currentChunk], e); | |||
| if (etags[currentChunk] != "") { | |||
| //更新数据库:分片上传结果 | |||
| await updateChunk(currentChunk); | |||
| } else { | |||
| return; | |||
| } | |||
| } else { | |||
| return; | |||
| } | |||
| } | |||
| }; | |||
| function completeUpload(){ | |||
| return new Promise((resolve, reject) => { | |||
| axios.post('/attachments/complete_multipart', qs.stringify({ | |||
| uuid: file.uuid, | |||
| uploadID: file.uploadID, | |||
| file_name: file.name, | |||
| size: file.size, | |||
| dataset_id: file.datasetId, | |||
| _csrf: csrf | |||
| })).then(function (response) { | |||
| resolve(response); | |||
| }).catch(function (error) { | |||
| console.log(error); | |||
| reject(error); | |||
| }); | |||
| }) | |||
| } | |||
| var successChunks = new Array(); | |||
| var successParts = new Array(); | |||
| successParts = file.chunks.split(","); | |||
| for (let i = 0; i < successParts.length; i++) { | |||
| successChunks[i] = successParts[i].split("-")[0].split("\"")[1]; | |||
| } | |||
| var urls = new Array(); | |||
| var etags = new Array(); | |||
| console.log('上传分片...'); | |||
| this.status='上传中'; | |||
| { | |||
| loadNext(); | |||
| fileReader.onload = async (e) => { | |||
| await uploadChunk(e); | |||
| currentChunk++; | |||
| if (currentChunk < chunks) { | |||
| console.log(`第${currentChunk}个分片上传完成, 开始第${currentChunk +1}/${chunks}个分片上传`); | |||
| this.progress = Math.ceil((currentChunk / chunks)*100); | |||
| await loadNext(); | |||
| } else { | |||
| await completeUpload(); | |||
| console.log(`文件上传完成:${file.name} \n分片:${chunks} 大小:${file.size} 用时:${(new Date().getTime() - time)/1000} s`); | |||
| this.progress = 100; | |||
| this.status='上传完成'; | |||
| window.location.reload(); | |||
| } | |||
| }; | |||
| } | |||
| }, | |||
| //计算MD5 | |||
| computeMD5(file) { | |||
| let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice, | |||
| chunkSize = 1024*1024*64, | |||
| chunks = Math.ceil(file.size / chunkSize), | |||
| currentChunk = 0, | |||
| spark = new SparkMD5.ArrayBuffer(), | |||
| fileReader = new FileReader(); | |||
| let time = new Date().getTime(); | |||
| console.log('计算MD5...') | |||
| this.status='计算MD5'; | |||
| file.totalChunkCounts = chunks; | |||
| loadNext(); | |||
| fileReader.onload = (e) => { | |||
| spark.append(e.target.result); // Append array buffer | |||
| currentChunk++; | |||
| if (currentChunk < chunks) { | |||
| console.log(`第${currentChunk}分片解析完成, 开始第${currentChunk +1}/${chunks}分片解析`); | |||
| loadNext(); | |||
| } else { | |||
| let md5 = spark.end(); | |||
| console.log(`MD5计算完成:${file.name} \nMD5:${md5} \n分片:${chunks} 大小:${file.size} 用时:${(new Date().getTime() - time)/1000} s`); | |||
| spark.destroy(); //释放缓存 | |||
| file.uniqueIdentifier = md5; //将文件md5赋值给文件唯一标识 | |||
| file.cmd5 = false; //取消计算md5状态 | |||
| this.computeMD5Success(file); | |||
| } | |||
| }; | |||
| fileReader.onerror = () => { | |||
| console.warn('oops, something went wrong.'); | |||
| file.cancel(); | |||
| }; | |||
| function loadNext() { | |||
| let start = currentChunk * chunkSize; | |||
| let end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize; | |||
| fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end)); | |||
| } | |||
| }, | |||
| async computeMD5Success(file) { | |||
| await this.getSuccessChunks(file); | |||
| if (file.uploadID == "" || file.uuid == "") { //未上传过 | |||
| await this.newMultiUpload(file); | |||
| if (file.uploadID != "" && file.uuid != "") { | |||
| file.chunks = ""; | |||
| this.multipartUpload(file); | |||
| } else { | |||
| //失败如何处理 | |||
| return; | |||
| } | |||
| } else { | |||
| if (file.uploaded == "1") { //已上传成功 | |||
| //秒传 | |||
| if (file.attachID == "0") { //删除数据集记录,未删除文件 | |||
| await addAttachment(file); | |||
| } | |||
| console.log("文件已上传完成"); | |||
| this.progress = 100; | |||
| this.status='上传完成'; | |||
| window.location.reload(); | |||
| } else { | |||
| //断点续传 | |||
| this.multipartUpload(file); | |||
| } | |||
| } | |||
| function addAttachment(file){ | |||
| return new Promise((resolve, reject) => { | |||
| axios.post('/attachments/add', qs.stringify({ | |||
| uuid: file.uuid, | |||
| file_name: file.name, | |||
| size: file.size, | |||
| dataset_id: file.datasetId, | |||
| _csrf: csrf | |||
| })).then(function (response) { | |||
| resolve(response); | |||
| }).catch(function (error) { | |||
| console.log(error); | |||
| reject(error); | |||
| }); | |||
| }) | |||
| } | |||
| } | |||
| } | |||
| } | |||
| </script> | |||
| <style> | |||
| .uploader-app { | |||
| width: 850px; | |||
| padding: 15px; | |||
| margin: 40px auto 0; | |||
| font-size: 12px; | |||
| box-shadow: 0 0 10px rgba(0, 0, 0, .4); | |||
| } | |||
| .uploader-app .uploader-btn { | |||
| margin-right: 40px; | |||
| } | |||
| .uploader-app .uploader-list { | |||
| max-height: 440px; | |||
| overflow: auto; | |||
| overflow-x: hidden; | |||
| overflow-y: auto; | |||
| } | |||
| </style> | |||
| @@ -0,0 +1,331 @@ | |||
| <template> | |||
| <div class="dropzone-wrapper"> | |||
| <div id="dropzone" class="dropzone"> | |||
| </div> | |||
| <p>文件处理状态:{{ status }}</p> | |||
| <p>文件上传进度:{{ progress }}%</p> | |||
| </div> | |||
| </template> | |||
| <script> | |||
| import Dropzone from 'dropzone/dist/dropzone.js'; | |||
| // import 'dropzone/dist/dropzone.css' | |||
| import createDropzone from '../features/dropzone.js'; | |||
| import SparkMD5 from 'spark-md5'; | |||
| import axios from 'axios'; | |||
| import qs from 'qs'; | |||
| const { | |||
| AppSubUrl, | |||
| StaticUrlPrefix, | |||
| csrf | |||
| } = window.config; | |||
| export default { | |||
| data() { | |||
| return { | |||
| dropzoneUploader: null, | |||
| maxFiles: 1, | |||
| maxFilesize: 1 * 1024 * 1024 * 1024 * 1024, | |||
| acceptedFiles: '*/*', | |||
| progress: 0, | |||
| status: '等待上传', | |||
| } | |||
| }, | |||
| async mounted() { | |||
| const dropzoneUploader = await createDropzone("div#dropzone", { | |||
| url: '/todouploader', | |||
| maxFiles: this.maxFiles, | |||
| maxFilesize: this.maxFileSize, | |||
| timeout: 0, | |||
| autoQueue: false, | |||
| }) | |||
| dropzoneUploader.on("addedfile", (file) => { | |||
| setTimeout(() => { | |||
| file.accepted && this.onFileAdded(file); | |||
| }, 200); | |||
| }); | |||
| dropzoneUploader.on("maxfilesexceeded", function(file) { | |||
| if (this.files[0].status !== 'success') { | |||
| alert('请等待第一个文件传输完成') | |||
| this.removeFile(file) | |||
| return | |||
| } | |||
| this.removeAllFiles(); | |||
| this.addFile(file); | |||
| }); | |||
| this.dropzoneUploader = dropzoneUploader | |||
| }, | |||
| methods: { | |||
| resetStatus() { | |||
| this.progress = 0 | |||
| this.status = '' | |||
| }, | |||
| updateProgress(file, progress) { | |||
| file.previewTemplate.querySelector(".dz-upload").style.width = `${progress}%`; | |||
| }, | |||
| emitDropzoneSuccess(file) { | |||
| file.status = Dropzone.SUCCESS; | |||
| this.dropzoneUploader.emit("success", file); | |||
| this.dropzoneUploader.emit("complete", file); | |||
| }, | |||
| onFileAdded(file) { | |||
| file.datasetId = document.getElementById("datasetId").getAttribute("datasetId"); | |||
| this.resetStatus() | |||
| this.computeMD5(file); | |||
| }, | |||
| finishUpload(file) { | |||
| this.emitDropzoneSuccess(file) | |||
| setTimeout(() => { | |||
| window.location.reload(); | |||
| }, 1000); | |||
| }, | |||
| computeMD5(file) { | |||
| this.resetStatus() | |||
| let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice, | |||
| chunkSize = 1024 * 1024 * 64, | |||
| chunks = Math.ceil(file.size / chunkSize), | |||
| currentChunk = 0, | |||
| spark = new SparkMD5.ArrayBuffer(), | |||
| fileReader = new FileReader(); | |||
| let time = new Date().getTime(); | |||
| // console.log('计算MD5...') | |||
| this.status = '计算MD5'; | |||
| file.totalChunkCounts = chunks; | |||
| loadNext(); | |||
| fileReader.onload = (e) => { | |||
| fileLoaded.call(this, e) | |||
| }; | |||
| fileReader.onerror = (err) => { | |||
| console.warn('oops, something went wrong.', err); | |||
| file.cancel(); | |||
| }; | |||
| function fileLoaded(e){ | |||
| spark.append(e.target.result); // Append array buffer | |||
| currentChunk++; | |||
| if (currentChunk < chunks) { | |||
| // console.log(`第${currentChunk}分片解析完成, 开始第${currentChunk +1}/${chunks}分片解析`); | |||
| this.status = `加载文件 ${(currentChunk/chunks*100).toFixed(2)}% (${currentChunk}/${chunks})`; | |||
| this.updateProgress(file, (currentChunk/chunks*100).toFixed(2)) | |||
| loadNext(); | |||
| return | |||
| } | |||
| let md5 = spark.end(); | |||
| console.log( | |||
| `MD5计算完成:${file.name} \nMD5:${md5} \n分片:${chunks} 大小:${file.size} 用时:${(new Date().getTime() - time)/1000} s` | |||
| ); | |||
| spark.destroy(); //释放缓存 | |||
| file.uniqueIdentifier = md5; //将文件md5赋值给文件唯一标识 | |||
| file.cmd5 = false; //取消计算md5状态 | |||
| this.computeMD5Success(file); | |||
| } | |||
| function loadNext() { | |||
| let start = currentChunk * chunkSize; | |||
| let end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize; | |||
| fileReader.readAsArrayBuffer(blobSlice.call(file, start, end)); | |||
| } | |||
| }, | |||
| async computeMD5Success(md5edFile) { | |||
| const file = await this.getSuccessChunks(md5edFile); | |||
| if (file.uploadID == "" || file.uuid == "") { //未上传过 | |||
| await this.newMultiUpload(file); | |||
| if (file.uploadID != "" && file.uuid != "") { | |||
| file.chunks = ""; | |||
| this.multipartUpload(file); | |||
| } else { | |||
| //失败如何处理 | |||
| return; | |||
| } | |||
| return | |||
| } | |||
| if (file.uploaded == "1") { //已上传成功 | |||
| //秒传 | |||
| if (file.attachID == "0") { //删除数据集记录,未删除文件 | |||
| await addAttachment(file); | |||
| } | |||
| console.log("文件已上传完成"); | |||
| this.progress = 100; | |||
| this.status = '上传完成'; | |||
| this.finishUpload(file) | |||
| } else { | |||
| //断点续传 | |||
| this.multipartUpload(file); | |||
| } | |||
| async function addAttachment(file) { | |||
| return await axios.post('/attachments/add', qs.stringify({ | |||
| uuid: file.uuid, | |||
| file_name: file.name, | |||
| size: file.size, | |||
| dataset_id: file.datasetId, | |||
| _csrf: csrf | |||
| })) | |||
| } | |||
| }, | |||
| async getSuccessChunks(file) { | |||
| const params = { | |||
| params: { | |||
| md5: file.uniqueIdentifier, | |||
| _csrf: csrf | |||
| } | |||
| } | |||
| try { | |||
| const response = await axios.get('/attachments/get_chunks', params) | |||
| file.uploadID = response.data.uploadID; | |||
| file.uuid = response.data.uuid; | |||
| file.uploaded = response.data.uploaded; | |||
| file.chunks = response.data.chunks; | |||
| file.attachID = response.data.attachID; | |||
| return file | |||
| } catch(error) { | |||
| console.log("getSuccessChunks catch: ", error); | |||
| return null | |||
| } | |||
| }, | |||
| async newMultiUpload(file) { | |||
| const res = await axios.get('/attachments/new_multipart', { | |||
| params: { | |||
| totalChunkCounts: file.totalChunkCounts, | |||
| md5: file.uniqueIdentifier, | |||
| size: file.size, | |||
| fileType: file.fileType, | |||
| _csrf: csrf | |||
| } | |||
| }) | |||
| file.uploadID = res.data.uploadID; | |||
| file.uuid = res.data.uuid; | |||
| }, | |||
| multipartUpload(file) { | |||
| let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice, | |||
| chunkSize = 1024 * 1024 * 64, | |||
| chunks = Math.ceil(file.size / chunkSize), | |||
| currentChunk = 0, | |||
| fileReader = new FileReader(), | |||
| time = new Date().getTime(); | |||
| function loadNext() { | |||
| let start = currentChunk * chunkSize; | |||
| let end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize; | |||
| fileReader.readAsArrayBuffer(blobSlice.call(file, start, end)); | |||
| } | |||
| function checkSuccessChunks() { | |||
| var index = successChunks.indexOf((currentChunk + 1).toString()) | |||
| if (index == -1) { | |||
| return false; | |||
| } | |||
| return true; | |||
| } | |||
| async function getUploadChunkUrl(currentChunk, partSize) { | |||
| const res = await axios.get('/attachments/get_multipart_url', { | |||
| params: { | |||
| uuid: file.uuid, | |||
| uploadID: file.uploadID, | |||
| size: partSize, | |||
| chunkNumber: currentChunk + 1, | |||
| _csrf: csrf | |||
| } | |||
| }) | |||
| console.log("getUploadChunkUrl: ", res) | |||
| urls[currentChunk] = res.data.url | |||
| } | |||
| async function uploadMinio(url, e) { | |||
| const res = await axios.put(url, e.target.result) | |||
| etags[currentChunk] = res.headers.etag; | |||
| } | |||
| async function updateChunk(currentChunk) { | |||
| await axios.post('/attachments/update_chunk', qs.stringify({ | |||
| uuid: file.uuid, | |||
| chunkNumber: currentChunk + 1, | |||
| etag: etags[currentChunk], | |||
| _csrf: csrf | |||
| })) | |||
| } | |||
| async function uploadChunk(e) { | |||
| if (!checkSuccessChunks()) { | |||
| let start = currentChunk * chunkSize; | |||
| let partSize = ((start + chunkSize) >= file.size) ? file.size - start : chunkSize; | |||
| //获取分片上传url | |||
| await getUploadChunkUrl(currentChunk, partSize); | |||
| if (urls[currentChunk] != "") { | |||
| //上传到minio | |||
| await uploadMinio(urls[currentChunk], e); | |||
| if (etags[currentChunk] != "") { | |||
| //更新数据库:分片上传结果 | |||
| await updateChunk(currentChunk); | |||
| } else { | |||
| return; | |||
| } | |||
| } else { | |||
| return; | |||
| } | |||
| } | |||
| }; | |||
| async function completeUpload() { | |||
| return await axios.post('/attachments/complete_multipart', qs.stringify({ | |||
| uuid: file.uuid, | |||
| uploadID: file.uploadID, | |||
| file_name: file.name, | |||
| size: file.size, | |||
| dataset_id: file.datasetId, | |||
| _csrf: csrf | |||
| })) | |||
| } | |||
| var successChunks = new Array(); | |||
| var successParts = new Array(); | |||
| successParts = file.chunks.split(","); | |||
| for (let i = 0; i < successParts.length; i++) { | |||
| successChunks[i] = successParts[i].split("-")[0].split("\"")[1]; | |||
| } | |||
| var urls = new Array(); | |||
| var etags = new Array(); | |||
| console.log('上传分片...'); | |||
| this.status = '上传中' | |||
| loadNext(); | |||
| fileReader.onload = async (e) => { | |||
| await uploadChunk(e); | |||
| currentChunk++; | |||
| if (currentChunk < chunks) { | |||
| console.log(`第${currentChunk}个分片上传完成, 开始第${currentChunk +1}/${chunks}个分片上传`); | |||
| this.progress = Math.ceil((currentChunk / chunks) * 100); | |||
| this.updateProgress(file, (currentChunk/chunks*100).toFixed(2)) | |||
| await loadNext(); | |||
| } else { | |||
| await completeUpload(); | |||
| console.log(`文件上传完成:${file.name} \n分片:${chunks} 大小:${file.size} 用时:${(new Date().getTime() - time)/1000} s`); | |||
| this.progress = 100; | |||
| this.status = '上传完成'; | |||
| this.finishUpload(file) | |||
| } | |||
| }; | |||
| }, | |||
| } | |||
| } | |||
| </script> | |||
| <style> | |||
| .dropzone-wrapper { | |||
| margin: 2em auto ; | |||
| } | |||
| </style> | |||
| @@ -8,6 +8,7 @@ import './polyfills.js'; | |||
| import Vue from 'vue'; | |||
| import 'jquery.are-you-sure'; | |||
| import './vendor/semanticdropdown.js'; | |||
| import VueSimpleUploader from 'vue-simple-uploader'; | |||
| import {svg} from './utils.js'; | |||
| import initContextPopups from './features/contextpopup.js'; | |||
| @@ -21,11 +22,9 @@ import highlight from './features/highlight.js'; | |||
| import ActivityTopAuthors from './components/ActivityTopAuthors.vue'; | |||
| import {initNotificationsTable, initNotificationCount} from './features/notification.js'; | |||
| import {createCodeEditor} from './features/codeeditor.js'; | |||
| import App from './App.vue' | |||
| import uploader from 'vue-simple-uploader' | |||
| Vue.use(uploader); | |||
| import MinioUploader from './components/MinioUploader.vue'; | |||
| Vue.use(VueSimpleUploader); | |||
| const {AppSubUrl, StaticUrlPrefix, csrf} = window.config; | |||
| @@ -3175,8 +3174,8 @@ function initVueUploader() { | |||
| /* eslint-disable no-new */ | |||
| new Vue({ | |||
| el: '#uploader', | |||
| components: { App }, | |||
| template: '<App/>' | |||
| components: {MinioUploader}, | |||
| template: '<MinioUploader />' | |||
| }); | |||
| } | |||