diff --git a/package.json b/package.json index 23b0953..9f5e8e8 100644 --- a/package.json +++ b/package.json @@ -21,11 +21,13 @@ "dingtalk-jsapi": "^3.0.20", "echarts": "5.2.2", "element-ui": "2.15.6", + "js-sha3": "^0.9.3", "jsonwebtoken": "^9.0.2", "nprogress": "0.2.0", "screenfull": "5.2.0", "serve": "^14.2.0", "sign-canvas": "1.1.4", + "spark-md5": "^3.0.2", "vue": "^2.6.14", "vue-cropper": "^0.6.2", "vue-cropperjs": "^2.2.2", diff --git a/src/com/cutUpload.js b/src/com/cutUpload.js new file mode 100644 index 0000000..c37e223 --- /dev/null +++ b/src/com/cutUpload.js @@ -0,0 +1,179 @@ +import { getProgress, postTask, getPartUrl, getMerge } from "/src/com/uploadApi"; +// import createOverallHase from '@/com/createOverallHase' +import SparkMD5 from 'spark-md5' +import axios from "axios"; + +const DEFAULT_SIZE = 20 * 1024 * 1024 +export function cutUpload (file, isFace = false, backUploadProgress){ + return new Promise((resolve, reject) => { + const PART_SIZE = 1024 * 1024 * 20; // 20M + // 计算上传进度 + const calculateProgress = (progressArr, total) => { + let count = 0; + progressArr.forEach((item) => { + count += item; + }); + if (count == total) { + count = Math.floor(total * 0.9999);//如果上传进度为100%,伪造上传进度,为合并文件做准备 + } + return count; + }; + // 分片上传函数 + const requestUploadFun = async (url, fileData, uploadProgress) => { + try { + await axios.put(url, fileData, { + headers: { + "Content-Type": "application/octet-stream", + }, + onUploadProgress: function (progressEvent) { + uploadProgress(progressEvent.loaded); + } + }); + } catch (error) { + console.error(error); + reject(error); + throw error; + } + }; + + const uploadChunks = async (file, identifier, progressData, uploadProgressFun) => { + const progressEvent = { total: file.size, loaded: 0 }; + const progressArr = []; + const partNumberSet = new Set(progressData.parts.map((item) => item.partNumber)); + const partCount = Math.ceil(file.size / PART_SIZE); + let totalSize = 0; + const partArr = []; + for (let i = 0; i < partCount; i++) { + let partNumber = i + 1; + const start = PART_SIZE * (partNumber - 1); + const end = Math.min(start + PART_SIZE, file.size); + if (!partNumberSet.has(partNumber)) { + const blob = file.slice(start, end); + totalSize += blob.size; + progressArr[partNumber] = 0; + partArr.push({ partNumber, blob }); + } else { + progressArr[partNumber] = end - start; + progressEvent.loaded = calculateProgress(progressArr, progressEvent.total); + uploadProgressFun(progressEvent); + } + } + + const uploadUrlDataArr = await Promise.all(partArr.map(async item => { + const urlData = await getPartUrl(identifier, item.partNumber); + return { + uploadUrl: urlData.uploadUrl, + fileData: item.blob, + partNumber: item.partNumber + } + })); + + const uploadPromises = uploadUrlDataArr.map(async (uploadUrlData, i) => { + return requestUploadFun(uploadUrlData.uploadUrl, uploadUrlData.fileData, function (loaded) { + progressArr[uploadUrlData.partNumber] = loaded; + progressEvent.loaded = calculateProgress(progressArr, progressEvent.total); + uploadProgressFun(progressEvent); + }); + }); + + await Promise.all(uploadPromises); + }; + + // 获取hash + const uploadFile = async (file, onUploadProgress = () => {}, checkFace, onCheckFace = () => {}) => { + if (file) { + const hashStr = await createOverallHase(file); + const fileForm = { + identifier: hashStr, + fileName: file.name, + fileSize: file.size.toString(), + checkFace: checkFace, + }; + uploadProgress(file, fileForm, onCheckFace, onUploadProgress) + } else { + console.log("文件为空"); + } + }; + + let obj = {} + uploadFile(file, function(Progress) { + backUploadProgress(Progress) + }, isFace, function(faceData){ + resolve({...obj, ...faceData}) + }) + + // 判断任务 是否存在 + const uploadProgress = async (file, fileForm, onCheckFace, onUploadProgressFun) => { + try { + const progressData = await getProgress(fileForm.identifier, fileForm.checkFace); + if (progressData.errMessage === "文件上传任务不存在") { + await postTask(fileForm); + await uploadProgress(file, fileForm, onCheckFace, onUploadProgressFun); + } else { + if (!progressData.finished) { + if (progressData.parts.length == progressData.totalPartNum) { + await getMerge(fileForm.identifier); + } else { + //const chunksArr = await uploadFile(file); + await uploadChunks(file, fileForm.identifier, progressData, onUploadProgressFun); + } + await uploadProgress(file, fileForm, onCheckFace, onUploadProgressFun); + } else { + let checkFaceMsg = progressData.checkFaceMsg + + // onUploadProgressFun({ total: Number(file.size), loaded: Number(file.size) }); + onUploadProgressFun({ total: Number(file.size), loaded: Number(file.size), path: progressData.path }) + if (fileForm.checkFace) { + // onCheckFace({ isFace: progressData.face, checkFaceMsg: progressData.checkFaceMsg }); + onCheckFace({ total: Number(file.size), loaded: Number(file.size), isFace: progressData.face, checkFaceMsg, path: progressData.path }) + } + // let checkFaceMsg = progressData.checkFaceMsg + // resolve({ total: Number(file.size), loaded: Number(file.size), isFace: progressData.face, checkFaceMsg, path: progressData.path }); + // console.log(progressData.path); + } + } + } catch (err) { + reject(err); + } + }; + + }) +} + +// md5 +const createOverallHase = (file, chunkSize = DEFAULT_SIZE) => { + return new Promise((resolve, reject) => { + const startMs = new Date().getTime(); + let blobSlice = + File.prototype.slice || + File.prototype.mozSlice || + File.prototype.webkitSlice; + let chunks = Math.ceil(file.size / chunkSize); + let currentChunk = 0; + let spark = new SparkMD5.ArrayBuffer(); //追加数组缓冲区。 + let fileReader = new FileReader(); //读取文件 + fileReader.onload = function (e) { + spark.append(e.target.result); + currentChunk++; + if (currentChunk < chunks) { + loadNext(); + } else { + const md5 = spark.end(); //完成md5的计算,返回十六进制结果。 + console.log('文件md5计算结束,总耗时:', (new Date().getTime() - startMs) / 1000, 's') + resolve(md5); + } + }; + fileReader.onerror = function (e) { + reject(e); + }; + + function loadNext() { + console.log('当前part number:', currentChunk, '总块数:', chunks); + let start = currentChunk * chunkSize; + let end = start + chunkSize; + (end > file.size) && (end = file.size); + fileReader.readAsArrayBuffer(blobSlice.call(file, start, end)); + } + loadNext(); + }); +} \ No newline at end of file diff --git a/src/com/uploadApi.js b/src/com/uploadApi.js new file mode 100644 index 0000000..a43d0cf --- /dev/null +++ b/src/com/uploadApi.js @@ -0,0 +1,35 @@ +import request from "/src/utils/request"; + + +// 查询分片上传进度 +export function getProgress(identifier,checkFace){ + return request({ + url: `/manager/file/task/${identifier}?checkFace=${checkFace}`, + method: 'GET', + }) +} + +// 初始化分片上传 +export function postTask(data){ + return request({ + url: '/manager/file/task', + method: 'POST', + data + }) +} +// 获取指定分片的上传地址 +export function getPartUrl(identifier, partNumber){ + return request({ + url: `/manager/file/task/part/${identifier}/${partNumber}`, + method: 'GET', + }) +} + +// 完成上传时调用 +export function getMerge(identifier){ + return request({ + url: `/manager/file/task/merge/${identifier}`, + method: 'GET', + }) +} + diff --git a/src/views/classList/classList/index.vue b/src/views/classList/classList/index.vue index fed75e3..7e568fd 100644 --- a/src/views/classList/classList/index.vue +++ b/src/views/classList/classList/index.vue @@ -35,7 +35,7 @@ 修改名称 删除 - 设置规则 + 设置规则 @@ -155,9 +155,14 @@ - + + 取 消 + 确 定 + + + @@ -165,14 +170,18 @@ import columns from "./columns"; import { getClass, getClassStudent, postManager, putManager, deleteManager, getGuardian } from "./api"; import { getRuleGroup } from "../../regulation/api"; - +import { Loading } from 'element-ui'; import { formatDate } from "@/com/index"; import CommonHead from './components/CommonHead.vue' +import { cutUpload } from "@/com/cutUpload"; +// import workerUrl from '@/com/worker.js'; +// import { cutFile } from '@/com/cutFile' const FlagEnum = { INTO: 0,// 进 OUT: 1,// 出 MORNING: 2, // 早上到校时间 }; +let loading export default { name: "deviceManagement", data() { @@ -217,8 +226,9 @@ export default { /* 设置规则组 */ isRuleDialog: false, // 进出校规则模态框 activeName: '', // 展开的规则组 - radio: '', + radio: '', // 校区选中的规则组 ruleGroupArr: [], // 规则组数据 + loadingText: '打包中' }; }, components: { @@ -235,8 +245,62 @@ export default { } }, mounted () { + // console.log(workerUrl) + // let worker = new Worker(workerUrl); }, methods: { + + async onClickFile(e) { + loading = Loading.service({ + lock: true, + text: '打包上传中...', + spinner: 'el-icon-loading', + background: 'rgba(0, 0, 0, 0.7)' + }); + const file = e.target.files[0] + + cutUpload(file, true, (progressEvent) =>{ + let progressNum = (progressEvent.loaded / progressEvent.total) * 100 + loading.text = '上传进度' + progressNum.toFixed(2) + '%' + if(progressNum == 100){ + console.log('完成') + loading.text = '上传完成!' + console.log(loading.text) + setTimeout(() => { + loading.close(); + }, 1000); + } + console.log(progressEvent.loaded + '/' + progressEvent.total); + }).then(res =>{ + console.log(res) + }) + + }, + // 点击确定修改小区规则 + onClickAffirmRule() { + console.log(this.radio) + if(!this.radio){ + this.$message.error('请选择需要设置的规则组') + return + } + this.ruleGroupForm.ruleGroupId = this.radio + putManager(this.ruleGroupForm).then(res =>{ + this.$message.success('修改成功') + this.classList(); + this.nodeData = this.ruleGroupForm.ruleGroupId + this.isRuleDialog = false + + }) + + }, + // 点击设置规则组 + onClickUpdateRule () { + this.isRuleDialog = true + console.log(this.nodeData) + this.ruleGroupForm = JSON.parse(JSON.stringify(this.nodeData)) + this.radio = this.ruleGroupForm.ruleGroupId + + }, // 规则组列表 ruleList(){ getRuleGroup().then( res =>{ diff --git a/yarn.lock b/yarn.lock index 6c5fa0c..89f3255 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5665,6 +5665,11 @@ js-message@1.0.7: resolved "https://registry.npmmirror.com/js-message/-/js-message-1.0.7.tgz" integrity sha512-efJLHhLjIyKRewNS9EGZ4UpI8NguuL6fKkhRxVuMmrGV2xN/0APGdQYwLFky5w9naebSZ0OwAGp0G6/2Cg90rA== +js-sha3@^0.9.3: + version "0.9.3" + resolved "https://registry.npmmirror.com/js-sha3/-/js-sha3-0.9.3.tgz#f0209432b23a66a0f6c7af592c26802291a75c2a" + integrity sha512-BcJPCQeLg6WjEx3FE591wVAevlli8lxsxm9/FzV4HXkV49TmBH38Yvrpce6fjbADGMKFrBMGTqrVz3qPIZ88Gg== + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz" @@ -8336,6 +8341,11 @@ source-map@^0.7.3: resolved "https://registry.npmmirror.com/source-map/-/source-map-0.7.4.tgz" integrity sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA== +spark-md5@^3.0.2: + version "3.0.2" + resolved "https://registry.npmmirror.com/spark-md5/-/spark-md5-3.0.2.tgz#7952c4a30784347abcee73268e473b9c0167e3fc" + integrity sha512-wcFzz9cDfbuqe0FZzfi2or1sgyIrsDwmPwfZC4hiNidPdPINjeUwNfv5kldczoEAcjl9Y1L3SM7Uz2PUEQzxQw== + spdx-correct@^3.0.0: version "3.2.0" resolved "https://registry.npmmirror.com/spdx-correct/-/spdx-correct-3.2.0.tgz"