前端大文件上传
大文件上传可能会有浏览器限制,网络稳定等问题影响用户体验,可以通过切片上传解决。
大文件上传可能会有浏览器限制,网络稳定等问题影响用户体验,可以通过切片上传解决。
一、切片上传
1. 原理
将大文件分割成较小的块(chunks),然后逐个上传这些块。这样可以减少单个上传请求的大小,降低网络故障和超时的风险。
在服务器端,可以接收这些块并将它们组合成完整的文件。
2. 实现步骤
2.1 文件分割
使用 JavaScript 的 `File` 对象和 `Blob` 类型来分割文件。可以根据一定的大小(如 1MB)将文件分割成多个块。
const file = document.getElementById("fileInput").files[0];
const chunkSize = 1024 * 1024; // 1MB
const chunks = [];
let start = 0;
while (start < file.size) {
const end = Math.min(start + chunkSize, file.size);
const chunk = file.slice(start, end);
chunks.push(chunk);
start = end;
}
2.2 逐个上传块
使用 `XMLHttpRequest` 或 `fetch` API 发送每个块的上传请求。可以为每个块设置一个唯一的标识符,以便服务器端能够正确地组合它们。
async function uploadChunks(chunks) {
for (let i = 0; i < chunks.length; i++) {
const chunk = chunks[i];
const formData = new FormData();
formData.append("chunk", chunk);
formData.append("index", i);
formData.append("totalChunks", chunks.length);
formData.append("fileName", file.name);
await fetch("/upload", {
method: "POST",
body: formData,
});
}
}
2.3 服务器端处理
在服务器端(如 Node.js),接收上传的块并将它们存储在临时目录中。根据块的索引和总数,将它们组合成完整的文件。
const fs = require("fs");
const path = require("path");
app.post("/upload", (req, res) => {
const chunk = req.files.chunk;
const index = req.body.index;
const totalChunks = req.body.totalChunks;
const fileName = req.body.fileName;
const filePath = path.join(__dirname, "uploads", fileName);
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.appendFileSync(filePath, chunk.data, {
flag: index === 0 ? "w" : "a",
});
if (index === totalChunks - 1) {
res.send("File uploaded successfully.");
} else {
res.sendStatus(200);
}
});
二、断点续传(Resume Upload)
1. 原理
如果上传过程中出现网络故障或其他问题,可以记录上传的进度,以便在下次上传时从上次中断的地方继续上传,而不是重新开始上传整个文件。
2. 实现步骤
2.1 记录进度
在前端,可以使用 `localStorage` 或其他存储机制来记录每个块的上传状态和进度。当一个块上传成功后,将其标记为已上传。
async function uploadChunks(chunks) {
for (let i = 0; i < chunks.length; i++) {
if (isChunkUploaded(i)) {
continue;
}
const chunk = chunks[i];
const formData = new FormData();
formData.append("chunk", chunk);
formData.append("index", i);
formData.append("totalChunks", chunks.length);
formData.append("fileName", file.name);
await fetch("/upload", {
method: "POST",
body: formData,
});
markChunkAsUploaded(i);
}
}
function isChunkUploaded(index) {
return localStorage.getItem(`chunkUploaded_${index}`) === "true";
}
function markChunkAsUploaded(index) {
localStorage.setItem(`chunkUploaded_${index}`, "true");
}
2.2 服务器端支持
服务器端需要能够识别哪些块已经上传,并在接收到新的块时正确地组合它们。可以通过检查文件的大小和块的索引来确定上传的进度。
app.post("/upload", (req, res) => {
const chunk = req.files.chunk;
const index = req.body.index;
const totalChunks = req.body.totalChunks;
const fileName = req.body.fileName;
const filePath = path.join(__dirname, "uploads", fileName);
fs.mkdirSync(path.dirname(filePath), { recursive: true });
if (
fs.existsSync(filePath) &&
fs.statSync(filePath).size >= index * chunk.length
) {
res.sendStatus(200);
return;
}
fs.appendFileSync(filePath, chunk.data, {
flag: index === 0 ? "w" : "a",
});
if (index === totalChunks - 1) {
res.send("File uploaded successfully.");
} else {
res.sendStatus(200);
}
});
三、进度显示
1. 原理
在上传过程中,向用户显示上传的进度,以便他们了解上传的状态。可以通过计算已上传的块的数量或已上传的字节数来显示进度。
2. 实现步骤
2.1 计算进度
在前端,可以通过计算已上传的块的数量与总块数的比例来显示进度。或者,可以计算已上传的字节数与文件总大小的比例。
async function uploadChunks(chunks) {
let uploadedBytes = 0;
for (let i = 0; i < chunks.length; i++) {
if (isChunkUploaded(i)) {
uploadedBytes += chunks[i].size;
continue;
}
const chunk = chunks[i];
const formData = new FormData();
formData.append("chunk", chunk);
formData.append("index", i);
formData.append("totalChunks", chunks.length);
formData.append("fileName", file.name);
const response = await fetch("/upload", {
method: "POST",
body: formData,
});
if (response.ok) {
uploadedBytes += chunk.size;
markChunkAsUploaded(i);
}
}
const progress = uploadedBytes / file.size;
updateProgressBar(progress);
}
function updateProgressBar(progress) {
const progressBar = document.getElementById("progressBar");
progressBar.style.width = `${progress * 100}%`;
}
2.2 显示进度
使用 HTML 和 CSS 来创建一个进度条或其他可视化元素,以显示上传的进度。可以根据计算出的进度值来更新进度条的宽度或显示其他进度信息。
<div
id="progressBar"
style="width: 0%; height: 10px; background-color: blue;"
></div>
总结:实现前端大文件上传需要综合考虑文件分割、断点续传和进度显示等方面。通过使用切片上传和断点续传技术,可以提高上传的可靠性和效率,同时向用户提供良好的上传体验。
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐

所有评论(0)