前端使用 spark-md5 实现大文件切片上传
需要计算文件MD5和、分片MD5:
封装公共方法代码如下:
import SparkMD5 from "spark-md5"/*** 计算文件MD5* @param file* @returns*/
export function calculateFileMD5(file) {return new Promise((resolve) => {const reader = new FileReader();reader.onload = function (e) {const spark = new SparkMD5.ArrayBuffer();spark.append(e.target.result);resolve(spark.end());};reader.readAsArrayBuffer(file);})
}/*** 计算分片MD5* @param chunk 当前切切片* @returns*/
export function calculateChunkMD5(chunk) {return new Promise((resolve) => {const reader = new FileReader();reader.onload = function (e) {const spark = new SparkMD5.ArrayBuffer();spark.append(e.target.result);resolve(spark.end());};reader.readAsArrayBuffer(chunk);})
}
在上传页面调用:
<template><form id="uploadForm"><div class="fileBox"><span class="fileLabel">文件: </span><input type="file" id="file" name="file"></div><el-progress id="progressBar" :percentage="progress" style="width: 335px;"></el-progress><button type="submit">提交</button></form>
</template><script setup>
import { calculateFileMD5, calculateChunkMD5 } from './calculateMd5'
import { ElMessage, ElMessageBox } from 'element-plus'
import axios from 'axios'
import { getToken } from "@/utils/auth";
const emit = defineEmits(['send-upload'])
const uploadChunkUrl = ref(import.meta.env.VITE_APP_BASE_API + "/dbms/file/chunk"); //上传接口
const mergeUrl = ref(import.meta.env.VITE_APP_BASE_API + "/dbms/file/merge"); //合并接口const file = ref(null)
const progress = ref(0)async function uploadFile(file) {const chunkSize = 5 * 1024 * 1024; // 5MB每片const totalChunks = Math.ceil(file.size / chunkSize);const fileMd5 = await calculateFileMD5(file); // 计算整个文件的MD5for (let i = 0; i < totalChunks; i++) {const start = i * chunkSize;const end = Math.min(file.size, start + chunkSize);const chunk = file.slice(start, end);const chunkMd5 = await calculateChunkMD5(chunk); // 计算当前分片的MD5progress.value = Math.round((i / totalChunks) * 100) // 计算上传进度const formData = new FormData();formData.append('file', chunk);formData.append('chunkNumber', i + 1);formData.append('totalChunks', totalChunks);formData.append('fileMd5', fileMd5);formData.append('chunkMd5', chunkMd5);formData.append('fileName', file.name);await axios.post(uploadChunkUrl.value, formData, {headers: { 'Content-Type': 'multipart/form-data', 'Authorization': 'Bearer ' + getToken() }});}// 所有分片上传完成后,通知后端合并const res = await axios.post(mergeUrl.value, {fileMd5: fileMd5,fileName: file.name,totalChunks: totalChunks});// console.log(res)if (res.status == 200) {progress.value = 100ElMessage.success('文件上传成功!')// 上传成功 通知父组件关闭弹窗,清空下拉表单,更新列表数据emit('send-upload')}
}onMounted(async () => {/**触发上传**/document.getElementById("uploadForm").onsubmit = async function (event) {event.preventDefault();const fileInput = document.getElementById("file");file.value = fileInput.files[0];if (!file.value) {ElMessage.warning("请选择文件!")return;}uploadFile(file.value)}
})
</script>