
1 安装依库 npm install spark-md5 --save
2 引入 import SparkMD5 from "spark-md5";
3pnpm install express multer cors
4 打开终端 cmd node启动 node bigfileserver.js
前端代码BigFileshangchuan.vue
<template>
<div class="upload-container">
<input type="file" @change="handleFileChange" />
<button @click="upload" :disabled="!file">上传</button>
<div v-if="progress > 0" class="progress">
上传进度:{{ progress.toFixed(2) }}%
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import SparkMD5 from "spark-md5";
// 1 安装依库 npm install spark-md5 --save
// 2 引入 import SparkMD5 from "spark-md5";
const file = ref(null);
const progress = ref(0);
const chunkSize = 3 * 1024 * 1024; // 每片3MB
function handleFileChange(e) {
file.value = e.target.files[0];
console.log("🚀 ~ handleFileChange ~ file.value :", file.value )
}
// 上传主流程
async function upload() {
if (!file.value) return;
console.log("🚀 ~ upload ~ file.value:", file.value)
const fileSize = file.value.size;
console.log("🚀 ~ upload ~ fileSize:", fileSize)
const chunkCount = Math.ceil(fileSize / chunkSize);
// 存储切片
const chunkAll = [];
const spark = new SparkMD5.ArrayBuffer();
// 逐片生成
for (let i = 0; i < chunkCount; i++) {
const chunkItem = await createChunk(file.value, i, chunkSize);
chunkAll.push(chunkItem);
progress.value = ((i + 1) / chunkCount) * 50; // 生成分片阶段进度
}
// 计算整文件MD5
const fullMd5 = await calculateFileMD5(file.value);
// 构造 FormData
const formData = new FormData();
chunkAll.forEach((item) => {
formData.append("chunk", item.blob, `chunk${item.index}`);
});
formData.append("MD5", fullMd5);
// 发送请求
// const res = await fetch("http://localhost:3000/upload", {
// method: "POST",
// body: formData,
// });
const res = await fetch("http://localhost:3000/upload", {
method: "POST",
body: formData,
});
const result = await res.json();
console.log(result);
progress.value = 100;
alert("上传完成!");
}
// 生成单个分片
function createChunk(file, index, chunkSize) {
return new Promise((resolve) => {
const start = index * chunkSize;
const end = Math.min(file.size, start + chunkSize);
const blob = file.slice(start, end);
const spark = new SparkMD5.ArrayBuffer();
const reader = new FileReader();
reader.readAsArrayBuffer(blob);
reader.onload = (e) => {
spark.append(e.target.result);
resolve({
index,
start,
end,
MD5: spark.end(),
blob,
});
};
});
}
// 计算整文件MD5
function calculateFileMD5(file) {
return new Promise((resolve) => {
const reader = new FileReader();
const spark = new SparkMD5.ArrayBuffer();
reader.readAsArrayBuffer(file);
reader.onload = (e) => {
spark.append(e.target.result);
resolve(spark.end());
};
});
}
</script>
<style scoped>
.upload-container {
padding: 20px;
}
button {
margin-left: 10px;
padding: 5px 10px;
}
.progress {
margin-top: 10px;
color: #2c7a7b;
}
</style>
模拟后端代码bigfileserver.js
/*
模拟后端大文件分片上传服务
1pnpm init -y
2pnpm install express multer cors
3 打开终端 cmd node启动 node bigfileserver.js
express:Web 框架
multer:处理 multipart/form-data(文件上传)
cors:允许前端跨域请求
*/
import express from "express";
import multer from "multer";
import cors from "cors";
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
// __dirname 在 ES Module 下需要自己定义
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const app = express();
const PORT = 3000;
app.use(cors());
// 设置上传目录
const upload = multer({ dest: "uploads/" });
// 用于记录上传开始时间
let startTime = null;
app.post("/upload", upload.array("chunk"), (req, res) => {
if (!startTime) startTime = Date.now();
let totalSize = 0;
console.log("接收到分片数量:", req.files.length);
// 保存分片,同时累加总文件大小
req.files.forEach((file) => {
const targetPath = path.join(__dirname, "uploads", file.originalname);
fs.renameSync(file.path, targetPath); // 保存分片
totalSize += file.size;
console.log(`分片: ${file.originalname}, 大小: ${(file.size / 1024 / 1024).toFixed(2)} MB`);
});
console.log(`本次请求上传总大小: ${(totalSize / 1024 / 1024).toFixed(2)} MB`);
// 判断是否最后一片,这里假设前端最后一个请求 MD5 最大值已传
const md5 = req.body.MD5;
if (md5) {
const endTime = Date.now();
console.log(`上传完成,总耗时: ${(endTime - startTime) / 1000}s`);
console.log(`整文件大小: ${(totalSize / 1024 / 1024).toFixed(2)} MB`);
startTime = null;
}
res.json({ code: 0, msg: "分片上传成功" });
});
app.listen(PORT, () => {
console.log(`Server running on http://localhost:${PORT}`);
});