随着互联网的发展,大文件上传已经成为了很多应用的必备功能,比如云存储、视频网站等。但是大文件上传会带来很多性能问题,比如上传时间长、占用带宽等。本文将介绍一些前端性能优化的技巧,让大文件上传更加快速、稳定和优化。
1. 分片上传
分片上传是将大文件切割成多个小文件,然后分别上传,最后在服务器端合并成一个完整的文件。这种方式可以减少单个请求的上传时间,提高上传速度。同时可以在上传过程中暂停、续传、重试等。
// www.javascriptcn.com code example
// 示例代码
function uploadFile(file) {
const chunkSize = 1024 * 1024; // 1MB
const totalChunks = Math.ceil(file.size / chunkSize);
let currentChunk = 0;
const upload = () => {
const start = currentChunk * chunkSize;
const end = Math.min(start + chunkSize, file.size);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append("chunk", chunk);
formData.append("totalChunks", totalChunks);
formData.append("currentChunk", currentChunk);
formData.append("filename", file.name);
axios.post("/upload", formData).then((response) => {
currentChunk++;
if (currentChunk < totalChunks) {
upload();
} else {
console.log("Upload completed");
}
});
};
upload();
}2. 断点续传
断点续传是指在上传过程中,如果因为网络等原因导致上传失败,可以从失败的地方继续上传,而不需要从头开始。这种方式可以减少上传时间,提高上传速度。
// www.javascriptcn.com code example
// 示例代码
function uploadFile(file) {
const chunkSize = 1024 * 1024; // 1MB
const totalChunks = Math.ceil(file.size / chunkSize);
let currentChunk = 0;
const upload = (start) => {
const end = Math.min(start + chunkSize, file.size);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append("chunk", chunk);
formData.append("totalChunks", totalChunks);
formData.append("currentChunk", currentChunk);
formData.append("filename", file.name);
axios.post("/upload", formData).then((response) => {
currentChunk++;
if (currentChunk < totalChunks) {
upload(end);
} else {
console.log("Upload completed");
}
});
};
// 检查是否有断点
axios.get(`/check/${file.name}`).then((response) => {
const start = response.data;
if (start > 0) {
currentChunk = Math.floor(start / chunkSize);
upload(start);
} else {
upload(0);
}
});
}3. 压缩上传
压缩上传是指在上传过程中,将文件进行压缩,减少文件大小,从而减少上传时间和占用带宽。这种方式需要在上传前先将文件进行压缩,然后在服务器端解压缩。
// www.javascriptcn.com code example
// 示例代码
function compressFile(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
const compressedFile = new File([pako.gzip(reader.result)], file.name, {
type: file.type,
lastModified: file.lastModified,
});
resolve(compressedFile);
};
reader.onerror = reject;
reader.readAsArrayBuffer(file);
});
}
function uploadFile(file) {
compressFile(file).then((compressedFile) => {
const formData = new FormData();
formData.append("file", compressedFile);
axios.post("/upload", formData).then((response) => {
console.log("Upload completed");
});
});
}4. 使用 Web Worker
大文件上传会占用很多主线程资源,导致页面卡顿,影响用户体验。使用 Web Worker 可以将上传过程放在后台线程中,不影响主线程的运行。这种方式需要在上传前先将文件进行切割,然后在 Web Worker 中上传。
// www.javascriptcn.com code example
// 示例代码
function createWorker(fn) {
const blob = new Blob([`(${fn.toString()})()`], { type: "text/javascript" });
const url = URL.createObjectURL(blob);
return new Worker(url);
}
function uploadFile(file) {
const chunkSize = 1024 * 1024; // 1MB
const totalChunks = Math.ceil(file.size / chunkSize);
let currentChunk = 0;
const worker = createWorker(() => {
self.addEventListener("message", (event) => {
const { chunk, totalChunks, currentChunk, filename } = event.data;
const formData = new FormData();
formData.append("chunk", chunk);
formData.append("totalChunks", totalChunks);
formData.append("currentChunk", currentChunk);
formData.append("filename", filename);
fetch("/upload", { method: "POST", body: formData }).then(() => {
self.postMessage({ currentChunk });
});
});
});
worker.addEventListener("message", (event) => {
currentChunk = event.data.currentChunk;
if (currentChunk < totalChunks) {
const start = currentChunk * chunkSize;
const end = Math.min(start + chunkSize, file.size);
const chunk = file.slice(start, end);
worker.postMessage({ chunk, totalChunks, currentChunk, filename: file.name });
} else {
console.log("Upload completed");
}
});
const start = currentChunk * chunkSize;
const end = Math.min(start + chunkSize, file.size);
const chunk = file.slice(start, end);
worker.postMessage({ chunk, totalChunks, currentChunk, filename: file.name });
}5. 使用 WebSocket
使用 WebSocket 可以实现实时上传进度的显示,同时可以在上传过程中暂停、续传、重试等。这种方式需要在服务器端实现 WebSocket 的接口。
// www.javascriptcn.com code example
// 示例代码
function uploadFile(file) {
const chunkSize = 1024 * 1024; // 1MB
const totalChunks = Math.ceil(file.size / chunkSize);
let currentChunk = 0;
const ws = new WebSocket("ws://localhost:3000/upload");
ws.addEventListener("open", () => {
const start = currentChunk * chunkSize;
const end = Math.min(start + chunkSize, file.size);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append("chunk", chunk);
formData.append("totalChunks", totalChunks);
formData.append("currentChunk", currentChunk);
formData.append("filename", file.name);
ws.send(formData);
});
ws.addEventListener("message", (event) => {
currentChunk++;
if (currentChunk < totalChunks) {
const start = currentChunk * chunkSize;
const end = Math.min(start + chunkSize, file.size);
const chunk = file.slice(start, end);
const formData = new FormData();
formData.append("chunk", chunk);
formData.append("totalChunks", totalChunks);
formData.append("currentChunk", currentChunk);
formData.append("filename", file.name);
ws.send(formData);
} else {
console.log("Upload completed");
}
});
}结语
大文件上传是一个比较复杂的问题,需要综合考虑网络、带宽、性能等因素。本文介绍了一些前端性能优化的技巧,包括分片上传、断点续传、压缩上传、使用 Web Worker 和使用 WebSocket。这些技巧可以让大文件上传更加快速、稳定和优化。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/67d2436aa941bf713444ea5e