复制粘贴就可以实现效果,找了这么多资源终于整合了一个靠谱的了!!!!!
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<script type="module" src="/node_modules/axios/dist/axios.js"></script>
</head>
<body>
<input type="file" id="fileInput" />
<!-- 设置一个进度条 -->
<progress value="0" max="100" id="progress"></progress>
<button onclick="upload()">上传文件</button>
<script>
async function upload() {
const fileInput = document.getElementById("fileInput"); //获取input框
const file = fileInput.files[0]; // 对input的文件进行获取
const chunkSize = 1 * 1024 * 1024; //初始化分块的尺寸 每块分块文件大小为1MB(1兆)
const totalChunks = Math.ceil(file.size / chunkSize); //通过文件尺寸计算出所有的块数
let currentChunk = 0; //设置块的初始值
const resumeKey = file.name + "-currentChunk"; // 用于断点续传的本地存储键名
const resumeIndex = localStorage.getItem(resumeKey); // 当前的索引值
if (resumeIndex != null) {
currentChunk = parseInt(resumeIndex); // 如果存在已处理的分片索引,则更新currentChunk
} else {
localStorage.removeItem(resumeKey); // 否则移除本地存储的键值对
}
// 通过while循环处理
while (currentChunk < totalChunks) {
console.log('currentChunk',currentChunk);
const start = currentChunk * chunkSize; // 计算当前块的起始位置
const end = Math.min(start + chunkSize, file.size); // 计算当前块的结束 Math.min:返回一组数值中的最小值
const chunk = file.slice(start, end); // 切割文件获取当前块
const formData = new FormData();
formData.append("file", chunk); // 添加当前块到 FormData 对象
formData.append("filename", file.name); // 添加文件名到 FormData 对象
formData.append("totalChunks", totalChunks); // 添加总块数到 FormData 对象
formData.append("currentChunk", currentChunk); // 添加当前块数到 FormData 对象
try {
const res = await axios.post(
"http://localhost:3000/upload",
formData,
{
headers: {
"Content-Type": "multipart/form-data",
},
}
); //发送当前块的上传请求
const { progress } = res.data; // 获取当前块的上传进度
document.getElementById("progress").value = progress; // 更新进度
// 保存当前分块的索引
localStorage.setItem(resumeKey, currentChunk);
} catch (error) {
console.error(error);
return;
}
currentChunk++; //增加当前块数,继续下一块的上传(实现循环操作)
}
// 上传完成后,移除localstorage中的分块索引
localStorage.removeItem(resumeKey);
// 当所有分块文件发送完毕,发起合并请求操作
try {
const postData = { filename: file.name, totalChunks: totalChunks }; //构造合并请求的数据
await axios.post("http://localhost:3000/merge", postData, {
headers: {
"Content-Type": "application/json",
},
}); //发送合并请求
} catch (error) {
console.error(error);
}
}
</script>
</body>
</html>
安装依赖
npm i express cors multer axios
服务器端
const express = require("express");
const cors = require("cors"); // 解决跨域问题
const path = require("path"); // 处理路径问题
const fs = require("fs"); // 文件读取操作
const multer = require("multer"); // 进行文件上传的操作处理
const upload = multer({ dest: "uploads/" }); // 设置文件上传地址
const bodyParser = require("body-parser"); // 实现body解析操作
const app = express();
app.use(cors());
app.use(bodyParser.json()); // 实现中间件body的应用
app.use(bodyParser.urlencoded({ extended: false })); // 实现文件路径的处理
app.listen(3000, () => {
console.log("Server started on port 3000");
});
app.post("/upload", upload.single("file"), (req, res) => {
console.log("uplaod!!");
const file = req.file; // 获取上传的文件对象
const filename = req.body.filename; // 获取文件名
const totalChunks = parseInt(req.body.totalChunks); // 获取总块数
const currentChunk = parseInt(req.body.currentChunk); //获取当前块数
const chunkPath = path.join("uploads/", `${filename}-chunk-${currentChunk}`); // 设置当前文件的存储路径
const chunkStream = fs.createReadStream(file.path); //创建读取文件块的可读流
const writeStream = fs.createWriteStream(chunkPath); //创建写入当前块的可写流
chunkStream.pipe(writeStream); //通过chunkStream.pipe管道操作,将读取的文件块内容通过管道写入当前块的文件
// 对分块上传内容结束以后的事件监听
chunkStream.on("end", () => {
fs.unlinkSync(file.path); //读取文件块的流结束后,删除临时文件
res.sendStatus(200); //响应上传成功的状态
});
});
app.post("/merge", (req, res) => {
const filename = req.body.filename; //获取文件名
const totalChunks = parseInt(req.body.totalChunks); //获取总块数
const mergedPath = path.join("uploads", filename); //生成合并后文件的存储路径
const writeStream = fs.createWriteStream(mergedPath); //创建写入合并后文件的可写流
// 合并文件块
const mergeChunks = (index) => {
if (index === totalChunks) {
writeStream.end(); //所有块都合并完成后,关闭写入流
res.sendStatus(200); //响应合并成功的状态
return;
}
const chunkPath = path.join("uploads", `${filename}-chunk-${index}`); //获取当前块的存储路径
const chunk = fs.readFileSync(chunkPath); //同步读取当前块的内容
fs.unlinkSync(chunkPath); //删除已合并的块文件
writeStream.write(chunk, () => {
mergeChunks(index + 1); //递归合并下一块
});
};
mergeChunks(0); //从第一块开始合并
});