From 90ddfba11bb78a67c471d30063aba174d6ef7c10 Mon Sep 17 00:00:00 2001 From: xiaotao Date: Sat, 7 Aug 2021 10:06:53 +0800 Subject: [PATCH 01/18] =?UTF-8?q?feat=20=E5=AE=9E=E7=8E=B0=E7=AE=80?= =?UTF-8?q?=E5=8D=95=E6=96=87=E4=BB=B6=E5=88=86=E5=9D=97=E4=B8=8A=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/API.js | 2 ++ src/api/breakpoint.js | 37 ++++++++++++++++++++++++ src/axios.config.js | 3 ++ src/global/FileQueue.js | 61 ++++++++++++++++++++++++---------------- src/utils/FileUtils.js | 15 ++++++++-- src/utils/FormUtils.d.ts | 8 ++++++ src/utils/FormUtils.js | 31 +++++++++++++++++++- 7 files changed, 130 insertions(+), 27 deletions(-) create mode 100644 src/api/breakpoint.js create mode 100644 src/utils/FormUtils.d.ts diff --git a/src/api/API.js b/src/api/API.js index cbfa785..3f1c7ff 100644 --- a/src/api/API.js +++ b/src/api/API.js @@ -2,6 +2,7 @@ const file = require('./file') const resource = require('./resource') const user = require('./user') const admin = require('./admin') +const breakpoint = require('./breakpoint') /** * @typedef {Object} FileTransferInfo 文件复制粘贴信息 @@ -18,6 +19,7 @@ const API = { file: file, resource: resource, admin: admin, + breakpoint: breakpoint, /** * 获取服务器地址,当服务器地址为空时,返回当前页面地址 */ diff --git a/src/api/breakpoint.js b/src/api/breakpoint.js new file mode 100644 index 0000000..f00d37e --- /dev/null +++ b/src/api/breakpoint.js @@ -0,0 +1,37 @@ +const breakpoint = { + /** + * 创建断点续传任务 + * @param {String} name 文件名 + * @param {Number} size 文件大小 + * @returns {import("axios").AxiosProxyConfig} + */ + createTask(name, size) { + return { + url: 'breakpoint', + method: 'post', + data: { + fileName: name, + length: size + }, + noDefaultAction: true + } + }, + /** + * 上传文件块到断点续传任务中 + * @param {String} id 断点续传任务ID + * @param {File} file 文件 + * @param {String} part 本次上传的文件编号,支持范围表示,如:3-6 表示本次上传的文件块按顺序包含编号3,4,5,6的部分 + * @returns {import("axios").AxiosRequestConfig} + */ + uploadPart(id, file, part) { + const fd = new FormData + fd.append('file', file) + return { + url: `breakpoint/${id}/${part}`, + method: 'post', + data: fd + } + } +} + +module.exports = breakpoint diff --git a/src/axios.config.js b/src/axios.config.js index 20400f5..b6e58de 100644 --- a/src/axios.config.js +++ b/src/axios.config.js @@ -34,6 +34,9 @@ axios.interceptors.response.use( case 0: mdui.alert(conf.data.msg) } + if (conf.config.noDefaultAction) { + return conf + } return Promise.reject(conf.data) }, err => { diff --git a/src/global/FileQueue.js b/src/global/FileQueue.js index c8daf26..f027aa7 100644 --- a/src/global/FileQueue.js +++ b/src/global/FileQueue.js @@ -2,7 +2,10 @@ const { default: Vue } = require('vue') const { default: mdui } = require('mdui') const { default: axios } = require('axios') const { default: FileUtils } = require('../utils/FileUtils') +const { default: FormUtils } = require('../utils/FormUtils') +// mdui.alert +const API = require('../api/API') /** * @typedef {Object} FileInfo * @property {File} file - 文件对象 @@ -49,6 +52,7 @@ const obj = { printInfo() { console.log(this.queue) }, + /** * 开始执行上传任务队列 * @todo 未做上传失败时的例外处理 @@ -97,10 +101,10 @@ const obj = { /** * 上传动作函数 */ - const uploadHandler = () => { + const uploadHandler = async () => { // 构造表单参数 const fd = new FormData() - fd.append('file', task.file) + // fd.append('file', task.file) fd.append('md5', task.md5) // 将文件信息中的params附加到表单中 for (const key in task.params) { @@ -109,38 +113,46 @@ const obj = { fd.append(key, value) } } + // 开始上传 + task.status = 'uploading' + const { taskId, chunkCount } = (await axios(API.breakpoint.createTask(task.file.name, task.file.size))).data + console.log(`任务ID:${taskId}`) + const generator = FileUtils.sliceFile(task.file) + let res + let cnt = 1 // 初始化事件记录 该记录用于上传测速 task.lastRecord = { date: new Date(), loaded: 0 } - // 开始上传 - task.status = 'uploading' - axios.put(task.api, fd, { - /** - * 实时更新上传进度和计算上传速度 - * @param {ProgressEvent} e - */ - onUploadProgress: e => { - const curr = new Date() - task.speed = (e.loaded - task.lastRecord.loaded) * 1000 / (curr - task.lastRecord.date) - task.prog = (e.loaded / e.total) * 100 - task.lastRecord = { - date: curr, - loaded: e.loaded - } - if (task.prog == 100) { - task.status = 'processing' - } + const updateProg = e => { + const curr = new Date() + task.speed = (e.loaded - task.lastRecord.loaded) * 1000 / (curr - task.lastRecord.date) + task.prog = (e.loaded / e.total) * 100 * ((cnt - 1)/chunkCount) + task.lastRecord = { + date: curr, + loaded: e.loaded } - }).then(e => { + if (task.prog == 100) { + task.status = 'processing' + } + } + while( !(res = generator.next()).done ) { + const blob = res.value + const conf = API.breakpoint.uploadPart(taskId, blob, cnt++) + conf.onUploadProgress = updateProg + await axios(conf) + updateProg({loaded: blob.size, total: blob.size}) + } + try { + await axios.put(`${task.api}/?breakpoint_id=${taskId}`, fd) // 上传成功 task.status = 'finish' Vue.prototype.$eventBus.$emit('uploaded', this.queue[0]) this.executing = false this.shift() this.executeQueue() - }).catch(e => { + } catch(e) { const msg = ` 错误:${e.msg}

文件名:${task.file.name}

@@ -158,9 +170,10 @@ const obj = { }, { modal: true }) - }) + } } } } -module.exports = obj +// module.exports = obj +export default obj \ No newline at end of file diff --git a/src/utils/FileUtils.js b/src/utils/FileUtils.js index fcbcc4a..c0f5c65 100644 --- a/src/utils/FileUtils.js +++ b/src/utils/FileUtils.js @@ -5,9 +5,20 @@ const md5 = require('js-md5') */ const FileUtils = { /** - * @callback GetFileListCallback - * @param {FileList} filelist 文件列表 + * 将文件按指定的块大小进行分割,文件最后一小块大小可能小于指定的块大小 + * @param {File} file 文件 + * @param {Number} chunkSize 每个分块的大小 + * @returns {Generator} */ + *sliceFile(file, chunkSize = 1024 * 1024 * 2) { + const fileSize = file.size + let index = 0 + while (index < fileSize) { + const end = index + Math.min(fileSize, chunkSize) + yield file.slice(index, end) + index = end + } + }, /** * 打开文件选择框 diff --git a/src/utils/FormUtils.d.ts b/src/utils/FormUtils.d.ts new file mode 100644 index 0000000..238785e --- /dev/null +++ b/src/utils/FormUtils.d.ts @@ -0,0 +1,8 @@ +declare module './FormUtils' { + function uploadSliceFileOpt(opt: UploadSliceFileOpt) +} +declare type UploadSliceFileOpt = { + file: File, + onprog: Function, + onblockfinish: Function +} diff --git a/src/utils/FormUtils.js b/src/utils/FormUtils.js index 4219fc9..8d753e3 100644 --- a/src/utils/FormUtils.js +++ b/src/utils/FormUtils.js @@ -1,4 +1,32 @@ + +/** + * @typedef {Object} UploadSliceFileOpt + * @property {File} file + * @property {Number} begin + * @property {String?} taskId + */ +import API from '../api/API' +import axios from '../axios.config' +import FileUtils from '../utils/FileUtils' const FormUtils = { + /** + * + * @param {UploadSliceFileOpt} param0 a + */ + async uploadSliceFile({ file, begin = 1, taskId }) { + const generator = FileUtils.sliceFile(file) + let res + let pos = 1 + if (!taskId) { + taskId = (await axios(API.breakpoint.createTask(file.name, file.size))).data.taskId + } + while ( !(res = generator.next()).done ) { + if (begin < pos) continue + const data = res.value + await axios(API.breakpoint.uploadPart(taskId, data, pos)) + } + return + }, /** * 使用POST带表单数据的方式打开新窗口 @@ -28,4 +56,5 @@ const FormUtils = { document.body.removeChild(form) } } -module.exports = FormUtils + +export default FormUtils \ No newline at end of file -- Gitee From f700b1061221daf448a02134e0f0da990e184303 Mon Sep 17 00:00:00 2001 From: xiaotao Date: Sat, 7 Aug 2021 12:26:19 +0800 Subject: [PATCH 02/18] =?UTF-8?q?fix=20=E4=BF=AE=E5=A4=8D=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=88=86=E5=9D=97=E7=BC=96=E5=8F=B7=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/FormUtils.d.ts | 8 -------- src/utils/FormUtils.js | 3 ++- 2 files changed, 2 insertions(+), 9 deletions(-) delete mode 100644 src/utils/FormUtils.d.ts diff --git a/src/utils/FormUtils.d.ts b/src/utils/FormUtils.d.ts deleted file mode 100644 index 238785e..0000000 --- a/src/utils/FormUtils.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -declare module './FormUtils' { - function uploadSliceFileOpt(opt: UploadSliceFileOpt) -} -declare type UploadSliceFileOpt = { - file: File, - onprog: Function, - onblockfinish: Function -} diff --git a/src/utils/FormUtils.js b/src/utils/FormUtils.js index 8d753e3..9b3a67e 100644 --- a/src/utils/FormUtils.js +++ b/src/utils/FormUtils.js @@ -21,9 +21,10 @@ const FormUtils = { taskId = (await axios(API.breakpoint.createTask(file.name, file.size))).data.taskId } while ( !(res = generator.next()).done ) { - if (begin < pos) continue + if (begin > pos) continue const data = res.value await axios(API.breakpoint.uploadPart(taskId, data, pos)) + pos++ } return }, -- Gitee From 8688ea62f2d099b6f642fd6dd9eb2cb63b3abee2 Mon Sep 17 00:00:00 2001 From: xiaotao Date: Mon, 9 Aug 2021 15:18:12 +0800 Subject: [PATCH 03/18] =?UTF-8?q?feat=20=E5=AE=8C=E6=88=90=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=8A=A8=E6=80=81=E5=88=86=E5=9D=97=E5=A4=A7=E5=B0=8F?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/global/FileQueue.js | 17 +++-------- src/utils/FormUtils.js | 62 +++++++++++++++++++++++++++++++++-------- 2 files changed, 55 insertions(+), 24 deletions(-) diff --git a/src/global/FileQueue.js b/src/global/FileQueue.js index f027aa7..7456c6c 100644 --- a/src/global/FileQueue.js +++ b/src/global/FileQueue.js @@ -115,12 +115,6 @@ const obj = { } // 开始上传 task.status = 'uploading' - const { taskId, chunkCount } = (await axios(API.breakpoint.createTask(task.file.name, task.file.size))).data - console.log(`任务ID:${taskId}`) - const generator = FileUtils.sliceFile(task.file) - let res - let cnt = 1 - // 初始化事件记录 该记录用于上传测速 task.lastRecord = { date: new Date(), loaded: 0 @@ -137,13 +131,10 @@ const obj = { task.status = 'processing' } } - while( !(res = generator.next()).done ) { - const blob = res.value - const conf = API.breakpoint.uploadPart(taskId, blob, cnt++) - conf.onUploadProgress = updateProg - await axios(conf) - updateProg({loaded: blob.size, total: blob.size}) - } + const taskId = await FormUtils.uploadSliceFile({ + file: task.file + }) + console.log(`上傳完成${taskId}`) try { await axios.put(`${task.api}/?breakpoint_id=${taskId}`, fd) // 上传成功 diff --git a/src/utils/FormUtils.js b/src/utils/FormUtils.js index 9b3a67e..e5b4269 100644 --- a/src/utils/FormUtils.js +++ b/src/utils/FormUtils.js @@ -8,25 +8,67 @@ import API from '../api/API' import axios from '../axios.config' import FileUtils from '../utils/FileUtils' -const FormUtils = { +const defsultChunkSize = 1024 * 1024 * 2 + +/** + * + * @param {Generator} file 文件切片生成器 + * @param {Number?|null} count 切片合并数量 + */ +function nextFileSlice(file, count = 1) { + let cnt = 0 + let tmp + let res = new Blob + while ( cnt < count && !(tmp = file.next()).done ) { + res = new Blob([res, tmp.value]) + cnt++ + } + console.log(`chunk count: ${res.size / defsultChunkSize}`) + return res.size == 0 ? null : res +} + +export default { /** * * @param {UploadSliceFileOpt} param0 a */ async uploadSliceFile({ file, begin = 1, taskId }) { const generator = FileUtils.sliceFile(file) - let res + let sliceMultipie = 1 let pos = 1 + let fileData + let chunkCount = Math.ceil(file.size/ (defsultChunkSize)) + + // 创建新任务ID if (!taskId) { taskId = (await axios(API.breakpoint.createTask(file.name, file.size))).data.taskId } - while ( !(res = generator.next()).done ) { - if (begin > pos) continue - const data = res.value - await axios(API.breakpoint.uploadPart(taskId, data, pos)) - pos++ + // 跳过前面部分 + for (let i = 1; i < pos; i++, pos++) { + generator.next() } - return + + let startNum = begin + let getPartNum = (startPos, count) => { + const endPos = startPos + count + return `${startPos == endPos ? startPos : `${startPos}-${endPos > chunkCount ? chunkCount : endPos}`}` + } + while ( (fileData = nextFileSlice(generator, sliceMultipie)) ) { + const start = Date.now() + const range = getPartNum(startNum, sliceMultipie) + console.log(`上传${range}`) + await axios(API.breakpoint.uploadPart(taskId, fileData, range)) + const executeTime = Date.now() - start + startNum += sliceMultipie + if (executeTime < 500) { + sliceMultipie ++ + } else if (executeTime > 5000 && sliceMultipie > 2) { + sliceMultipie -- + } + + console.log(`分块${range}上传耗时:${executeTime}`) + } + return taskId }, /** @@ -56,6 +98,4 @@ const FormUtils = { form.submit() document.body.removeChild(form) } -} - -export default FormUtils \ No newline at end of file +} \ No newline at end of file -- Gitee From b16bfbad856929dfb3d588c3dd234ffc9cfaf725 Mon Sep 17 00:00:00 2001 From: xiaotao Date: Tue, 10 Aug 2021 23:07:23 +0800 Subject: [PATCH 04/18] =?UTF-8?q?perf=20=E4=BC=98=E5=8C=96=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E5=88=87=E7=89=87=E5=88=86=E5=9D=97=E4=B8=8A=E4=BC=A0?= =?UTF-8?q?=E7=9A=84=E5=8A=A8=E6=80=81=E5=80=8D=E6=95=B0=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/global/FileQueue.js | 7 ++-- src/utils/FormUtils.js | 73 +++++++++++++++++++++++++++++++++-------- 2 files changed, 62 insertions(+), 18 deletions(-) diff --git a/src/global/FileQueue.js b/src/global/FileQueue.js index 7456c6c..db0cff5 100644 --- a/src/global/FileQueue.js +++ b/src/global/FileQueue.js @@ -104,7 +104,6 @@ const obj = { const uploadHandler = async () => { // 构造表单参数 const fd = new FormData() - // fd.append('file', task.file) fd.append('md5', task.md5) // 将文件信息中的params附加到表单中 for (const key in task.params) { @@ -122,7 +121,7 @@ const obj = { const updateProg = e => { const curr = new Date() task.speed = (e.loaded - task.lastRecord.loaded) * 1000 / (curr - task.lastRecord.date) - task.prog = (e.loaded / e.total) * 100 * ((cnt - 1)/chunkCount) + task.prog = (e.loaded / e.total) * 100 task.lastRecord = { date: curr, loaded: e.loaded @@ -132,9 +131,9 @@ const obj = { } } const taskId = await FormUtils.uploadSliceFile({ - file: task.file + file: task.file, + onUploadProg: updateProg }) - console.log(`上傳完成${taskId}`) try { await axios.put(`${task.api}/?breakpoint_id=${taskId}`, fd) // 上传成功 diff --git a/src/utils/FormUtils.js b/src/utils/FormUtils.js index e5b4269..1326f85 100644 --- a/src/utils/FormUtils.js +++ b/src/utils/FormUtils.js @@ -1,10 +1,24 @@ +/** + * @typedef {Object} ProgressInfo + * @property {Number} total + * @property {Number} loaded + */ + +/** + * @callback UploadProgress + * @param {ProgressInfo} e + */ + /** * @typedef {Object} UploadSliceFileOpt * @property {File} file * @property {Number} begin * @property {String?} taskId + * @property {UploadProgress} onUploadProg + * @property {UploadProgress} onBlockFinish */ + import API from '../api/API' import axios from '../axios.config' import FileUtils from '../utils/FileUtils' @@ -23,7 +37,6 @@ function nextFileSlice(file, count = 1) { res = new Blob([res, tmp.value]) cnt++ } - console.log(`chunk count: ${res.size / defsultChunkSize}`) return res.size == 0 ? null : res } @@ -32,7 +45,15 @@ export default { * * @param {UploadSliceFileOpt} param0 a */ - async uploadSliceFile({ file, begin = 1, taskId }) { + async uploadSliceFile({ file, begin = 1, taskId, onUploadProg, onBlockFinish }) { + const record = { + total: file.size, + loaded: 0 + } + const chunkRecord = { + total: 0, + loaded: 0 + } const generator = FileUtils.sliceFile(file) let sliceMultipie = 1 let pos = 1 @@ -45,28 +66,52 @@ export default { } // 跳过前面部分 for (let i = 1; i < pos; i++, pos++) { - generator.next() + record.loaded += generator.next().value.size } let startNum = begin - let getPartNum = (startPos, count) => { - const endPos = startPos + count - return `${startPos == endPos ? startPos : `${startPos}-${endPos > chunkCount ? chunkCount : endPos}`}` + const getPartNum = (startPos, count) => { + let endPos = startPos + count + if (endPos > chunkCount) { + endPos = chunkCount + } + return `${startPos == endPos ? startPos : `${startPos}-${endPos}`}` + } + const emit = isChunkFinish => { + if (isChunkFinish) { + record.loaded += chunkRecord.total, + onBlockFinish && onBlockFinish(record) + onUploadProg && onUploadProg(record) + } else { + const data = { + total: record.total, + loaded: record.loaded + chunkRecord.loaded + } + onUploadProg && onUploadProg(data) + } + } while ( (fileData = nextFileSlice(generator, sliceMultipie)) ) { + chunkRecord.total = fileData.size + chunkRecord.loaded = 0 const start = Date.now() const range = getPartNum(startNum, sliceMultipie) - console.log(`上传${range}`) - await axios(API.breakpoint.uploadPart(taskId, fileData, range)) + const conf = API.breakpoint.uploadPart(taskId, fileData, range) + conf.onUploadProgress = e => { + chunkRecord.loaded = e.loaded + emit() + } + await axios(conf) + emit(true) const executeTime = Date.now() - start startNum += sliceMultipie - if (executeTime < 500) { - sliceMultipie ++ - } else if (executeTime > 5000 && sliceMultipie > 2) { - sliceMultipie -- - } - console.log(`分块${range}上传耗时:${executeTime}`) + + if (executeTime < 1500 || executeTime > 5000) { + const change = Math.ceil(1500 / (executeTime / sliceMultipie) ) + console.log(`${sliceMultipie} -> ${change} time: ${executeTime}`) + sliceMultipie = change + } } return taskId }, -- Gitee From 4a3f0bfbd37b8f784b8acce5df2e75626f0cdcf1 Mon Sep 17 00:00:00 2001 From: xiaotao Date: Wed, 11 Aug 2021 18:41:43 +0800 Subject: [PATCH 05/18] =?UTF-8?q?refactor=20=E8=B0=83=E6=95=B4FormUtils?= =?UTF-8?q?=E5=B7=A5=E5=85=B7=E7=B1=BB=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/FormUtils.js | 146 ------------------------- src/utils/FormUtils/CommonFormUtils.js | 31 ++++++ src/utils/FormUtils/FileFormUtils.js | 123 +++++++++++++++++++++ src/utils/FormUtils/index.js | 7 ++ 4 files changed, 161 insertions(+), 146 deletions(-) delete mode 100644 src/utils/FormUtils.js create mode 100644 src/utils/FormUtils/CommonFormUtils.js create mode 100644 src/utils/FormUtils/FileFormUtils.js create mode 100644 src/utils/FormUtils/index.js diff --git a/src/utils/FormUtils.js b/src/utils/FormUtils.js deleted file mode 100644 index 1326f85..0000000 --- a/src/utils/FormUtils.js +++ /dev/null @@ -1,146 +0,0 @@ -/** - * @typedef {Object} ProgressInfo - * @property {Number} total - * @property {Number} loaded - */ - -/** - * @callback UploadProgress - * @param {ProgressInfo} e - */ - - -/** - * @typedef {Object} UploadSliceFileOpt - * @property {File} file - * @property {Number} begin - * @property {String?} taskId - * @property {UploadProgress} onUploadProg - * @property {UploadProgress} onBlockFinish - */ - -import API from '../api/API' -import axios from '../axios.config' -import FileUtils from '../utils/FileUtils' -const defsultChunkSize = 1024 * 1024 * 2 - -/** - * - * @param {Generator} file 文件切片生成器 - * @param {Number?|null} count 切片合并数量 - */ -function nextFileSlice(file, count = 1) { - let cnt = 0 - let tmp - let res = new Blob - while ( cnt < count && !(tmp = file.next()).done ) { - res = new Blob([res, tmp.value]) - cnt++ - } - return res.size == 0 ? null : res -} - -export default { - /** - * - * @param {UploadSliceFileOpt} param0 a - */ - async uploadSliceFile({ file, begin = 1, taskId, onUploadProg, onBlockFinish }) { - const record = { - total: file.size, - loaded: 0 - } - const chunkRecord = { - total: 0, - loaded: 0 - } - const generator = FileUtils.sliceFile(file) - let sliceMultipie = 1 - let pos = 1 - let fileData - let chunkCount = Math.ceil(file.size/ (defsultChunkSize)) - - // 创建新任务ID - if (!taskId) { - taskId = (await axios(API.breakpoint.createTask(file.name, file.size))).data.taskId - } - // 跳过前面部分 - for (let i = 1; i < pos; i++, pos++) { - record.loaded += generator.next().value.size - } - - let startNum = begin - const getPartNum = (startPos, count) => { - let endPos = startPos + count - if (endPos > chunkCount) { - endPos = chunkCount - } - return `${startPos == endPos ? startPos : `${startPos}-${endPos}`}` - } - const emit = isChunkFinish => { - if (isChunkFinish) { - record.loaded += chunkRecord.total, - onBlockFinish && onBlockFinish(record) - onUploadProg && onUploadProg(record) - } else { - const data = { - total: record.total, - loaded: record.loaded + chunkRecord.loaded - } - onUploadProg && onUploadProg(data) - } - - } - while ( (fileData = nextFileSlice(generator, sliceMultipie)) ) { - chunkRecord.total = fileData.size - chunkRecord.loaded = 0 - const start = Date.now() - const range = getPartNum(startNum, sliceMultipie) - const conf = API.breakpoint.uploadPart(taskId, fileData, range) - conf.onUploadProgress = e => { - chunkRecord.loaded = e.loaded - emit() - } - await axios(conf) - emit(true) - const executeTime = Date.now() - start - startNum += sliceMultipie - - - if (executeTime < 1500 || executeTime > 5000) { - const change = Math.ceil(1500 / (executeTime / sliceMultipie) ) - console.log(`${sliceMultipie} -> ${change} time: ${executeTime}`) - sliceMultipie = change - } - } - return taskId - }, - - /** - * 使用POST带表单数据的方式打开新窗口 - * @param {Stirng} url 目标地址 - * @param {Boolean} newWindow 是否在新窗口打开 - * @param {Object} filed 附加的表单字段 - */ - jumpWithPost(url, newWindow, filed) { - const form = document.createElement('form') - form.action = url - form.method = 'post' - if (newWindow) { - form.target = '_blank' - } - for (const key in filed) { - if (Object.hasOwnProperty.call(filed, key)) { - const value = filed[key] - const input = document.createElement('input') - input.name = key - input.value = value - form.appendChild(input) - } - } - form.style.display = 'none' - document.body.appendChild(form) - form.submit() - document.body.removeChild(form) - } -} \ No newline at end of file diff --git a/src/utils/FormUtils/CommonFormUtils.js b/src/utils/FormUtils/CommonFormUtils.js new file mode 100644 index 0000000..a087e7b --- /dev/null +++ b/src/utils/FormUtils/CommonFormUtils.js @@ -0,0 +1,31 @@ +/** + * 使用POST带表单数据的方式打开新窗口 + * @param {String} url 目标地址 + * @param {Boolean} newWindow 是否在新窗口打开 + * @param {Object} filed 附加的表单字段 + */ +function jumpWithPost(url, newWindow, filed) { + const form = document.createElement('form') + form.action = url + form.method = 'post' + if (newWindow) { + form.target = '_blank' + } + for (const key in filed) { + if (Object.hasOwnProperty.call(filed, key)) { + const value = filed[key] + const input = document.createElement('input') + input.name = key + input.value = value + form.appendChild(input) + } + } + form.style.display = 'none' + document.body.appendChild(form) + form.submit() + document.body.removeChild(form) +} + +export { + jumpWithPost +} diff --git a/src/utils/FormUtils/FileFormUtils.js b/src/utils/FormUtils/FileFormUtils.js new file mode 100644 index 0000000..6522e18 --- /dev/null +++ b/src/utils/FormUtils/FileFormUtils.js @@ -0,0 +1,123 @@ +/** + * @typedef {Object} ProgressInfo + * @property {Number} total + * @property {Number} loaded + */ + +/** + * @callback UploadProgress + * @param {ProgressInfo} e + */ + + +/** + * @typedef {Object} UploadSliceFileOpt + * @property {File} file + * @property {Number} begin + * @property {String?} taskId + * @property {UploadProgress} onUploadProg + * @property {UploadProgress} onBlockFinish + */ + +import API from '@/api/API' +import axios from '@/axios.config' +import FileUtils from '@/utils/FileUtils' +const defsultChunkSize = 1024 * 1024 * 2 + +/** + * + * @param {Generator} file 文件切片生成器 + * @param {Number?|null} count 切片合并数量 + */ +function nextFileSlice(file, count = 1) { + let cnt = 0 + let tmp + let res = new Blob + while ( cnt < count && !(tmp = file.next()).done ) { + res = new Blob([res, tmp.value]) + cnt++ + } + return res.size == 0 ? null : res +} + + +/** + * + * @param {UploadSliceFileOpt} param0 a + */ +async function uploadSliceFile({ file, begin = 1, taskId, onUploadProg, onBlockFinish }) { + const record = { + total: file.size, + loaded: 0 + } + const chunkRecord = { + total: 0, + loaded: 0 + } + const generator = FileUtils.sliceFile(file) + let sliceMultipie = 1 + let pos = 1 + let fileData + let chunkCount = Math.ceil(file.size/ (defsultChunkSize)) + + // 创建新任务ID + if (!taskId) { + taskId = (await axios(API.breakpoint.createTask(file.name, file.size))).data.taskId + } + // 跳过前面部分 + for (let i = 1; i < pos; i++, pos++) { + record.loaded += generator.next().value.size + } + + let startNum = begin + const getPartNum = (startPos, count) => { + let endPos = startPos + count + if (endPos > chunkCount) { + endPos = chunkCount + } + return `${startPos == endPos ? startPos : `${startPos}-${endPos}`}` + } + + const emit = isChunkFinish => { + if (isChunkFinish) { + record.loaded += chunkRecord.total, + onBlockFinish && onBlockFinish(record) + onUploadProg && onUploadProg(record) + } else { + const data = { + total: record.total, + loaded: record.loaded + chunkRecord.loaded + } + onUploadProg && onUploadProg(data) + } + + } + + while ( (fileData = nextFileSlice(generator, sliceMultipie)) ) { + chunkRecord.total = fileData.size + chunkRecord.loaded = 0 + const start = Date.now() + const range = getPartNum(startNum, sliceMultipie) + const conf = API.breakpoint.uploadPart(taskId, fileData, range) + conf.onUploadProgress = e => { + chunkRecord.loaded = e.loaded + emit() + } + await axios(conf) + emit(true) + const executeTime = Date.now() - start + startNum += sliceMultipie + + + if (executeTime < 1500 || executeTime > 5000) { + const change = Math.ceil(1500 / (executeTime / sliceMultipie) ) + console.log(`${sliceMultipie} -> ${change} time: ${executeTime}`) + sliceMultipie = change + } + } + return taskId +} + +export { + uploadSliceFile +} diff --git a/src/utils/FormUtils/index.js b/src/utils/FormUtils/index.js new file mode 100644 index 0000000..53a1801 --- /dev/null +++ b/src/utils/FormUtils/index.js @@ -0,0 +1,7 @@ +import { jumpWithPost } from "./CommonFormUtils" +import { uploadSliceFile } from "./FileFormUtils" + +export default { + uploadSliceFile, + jumpWithPost +} -- Gitee From a7d662cc15e27b22cec1f04251ce05c4f723c5ea Mon Sep 17 00:00:00 2001 From: xiaotao Date: Wed, 11 Aug 2021 18:42:14 +0800 Subject: [PATCH 06/18] =?UTF-8?q?chore=20=E6=B7=BB=E5=8A=A0vscode=20JavaSc?= =?UTF-8?q?ript=E6=8B=93=E5=B1=95=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= =?UTF-8?q?=E4=BB=A5=E6=94=AF=E6=8C=81webpack=E7=9A=84@=E8=B7=AF=E5=BE=84a?= =?UTF-8?q?lias?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jsconfig.json | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 jsconfig.json diff --git a/jsconfig.json b/jsconfig.json new file mode 100644 index 0000000..0898bab --- /dev/null +++ b/jsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "paths": { + "@/*": ["src/*"] + }, + "target": "ES6", + "allowSyntheticDefaultImports": true + } +} \ No newline at end of file -- Gitee From d7314203dc0c9fa06b98c8ec4c5bbcd4a6e5e3be Mon Sep 17 00:00:00 2001 From: xiaotao Date: Thu, 12 Aug 2021 16:30:24 +0800 Subject: [PATCH 07/18] =?UTF-8?q?chore=20=E8=AE=BE=E7=BD=AEvscode=E7=9A=84?= =?UTF-8?q?JavaScript=E6=8B=93=E5=B1=95baseUrl=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- jsconfig.json | 1 + 1 file changed, 1 insertion(+) diff --git a/jsconfig.json b/jsconfig.json index 0898bab..f0e2502 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -1,5 +1,6 @@ { "compilerOptions": { + "baseUrl": "./", "paths": { "@/*": ["src/*"] }, -- Gitee From 5af1b47a4d0eb6c25eae4e5d98e1f4a51fced533 Mon Sep 17 00:00:00 2001 From: xiaotao Date: Thu, 12 Aug 2021 18:11:51 +0800 Subject: [PATCH 08/18] Merge commit '797aa9dd8f4d2bf85ea663ff48bd6c8b6542f9e5' --- src/components/FileHandler.vue | 10 +- .../index.vue} | 6 +- src/components/layout/Drawer.vue | 102 ++++++++++++++++++ src/view/common/index.vue | 97 +++-------------- 4 files changed, 124 insertions(+), 91 deletions(-) rename src/components/{FileUploadDialog.vue => FileUploadDialog/index.vue} (97%) create mode 100644 src/components/layout/Drawer.vue diff --git a/src/components/FileHandler.vue b/src/components/FileHandler.vue index c5dd25a..06581ee 100644 --- a/src/components/FileHandler.vue +++ b/src/components/FileHandler.vue @@ -60,13 +60,13 @@ + + \ No newline at end of file diff --git a/src/view/common/index.vue b/src/view/common/index.vue index 875cbff..dec51f6 100644 --- a/src/view/common/index.vue +++ b/src/view/common/index.vue @@ -1,71 +1,9 @@