封装文件上传分包

This commit is contained in:
xiayuping666
2024-05-07 17:17:37 +08:00
parent c0efa47d1f
commit 8efb16a791
5 changed files with 294 additions and 4 deletions
+179
View File
@@ -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();
});
}
+35
View File
@@ -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',
})
}
+68 -4
View File
@@ -35,7 +35,7 @@
<!-- <div v-else style="margin-right: 10px; font-size: 16px; line-height: 27px;font-weight: 700;">{{ classData.className}}</div> -->
<el-button @click="onClickUpdate" v-if="nodeData.className" size="mini" type="warning" class="el-icon-edit">修改名称</el-button>
<el-button @click="onClickRemove" v-if="nodeData.className" size="mini" type="danger" class="el-icon-delete">删除</el-button>
<el-button @click="isRuleDialog = true" v-if="nodeData.className" size="mini" type="primary" class="el-icon-setting">设置规则</el-button>
<el-button @click="onClickUpdateRule" v-if="nodeData.className && nodeData.superId === '0'" size="mini" type="primary" class="el-icon-setting">设置规则</el-button>
</div>
</el-card>
@@ -155,9 +155,14 @@
</el-table-column>
</el-table>
</el-collapse-item>
</el-collapse>
<span slot="footer" class="dialog-footer">
<el-button @click="isRuleDialog = false">取 消</el-button>
<el-button type="primary" @click="onClickAffirmRule">确 定</el-button>
</span>
</el-dialog>
<input @change="onClickFile($event)" type="file">
</div>
</template>
@@ -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 =>{