常规项目日常开发的时候我们经常用到上传这个功能,一般日常的话不会有很大的文件上传,但是就是有不长眼的需求过来。 ==
切片上传优点
- 大文件分割,切片后分批上传,减轻服务器压力
- 断点续传,可以记录上传位置,方便上次上传未结束的操作
- 上传进度控制的功能,不然无法知道文件是否上传的进度。
为什么使用web worker
- 让线程不阻塞 ,切片 和 上传大文件是一个比较耗费时间的操作,如果在主要线程直行这个操作,容易卡死和卡顿,影响体验和效果
- 提高性能充分利用处理器
切片主要代码演示
import React, { useEffect } from "react";
const Login: React.FC = () => {
const chunkSize = 1 * 1024 * 1024; // 1M
// 将文件切片
const sliceFile = (file: File) => {
// 切片的内容存放
const chunks = [];
// 切片的位置
let offset = 0;
while (offset < file.size) {
// 分割切片位置以及每次切片的大小
const chunk = file.slice(offset, offset + chunkSize);
chunks.push(chunk);
// 切片的位置计算
offset = offset + chunkSize;
}
return chunks;
};
// 上传的切片
const uploadChunks = async (chunks: any) => {
for (let i = 0; i < chunks.length; i++) {
const formData = new FormData();
formData.append("fileChunk", chunks[i]);
// 发送切片上传请求
await fetch("/update", {
method: "POST",
body: formData,
});
}
};
// 上传的主函数
const uploadFile = async (file: File) => {
const chunks = await sliceFile(file);
await uploadChunks(chunks);
};
const click = () => {
document.getElementById("upload")?.click();
};
useEffect(() => {
document.getElementById("upload")?.addEventListener("change", (e) => {
// 获得当前上传的文件
const files = (e.target as HTMLInputElement).files?.[0];
if (files) {
uploadFile(files);
}
});
}, []);
return (
<div className="login">
<h1>大文件上传</h1>
<button onClick={click}> 选择上传文件</button>
<input type="file" id="upload" style={{ display: "none" }} />
</div>
);
};
export default Login;
效果如下
成功切片上传 但是会影响到页面卡顿 使用webworker
//worker.ts
// 上传的切片
const uploadChunks = async (chunks: any) => {
for (let i = 0; i < chunks.length; i++) {
const formData = new FormData();
formData.append("fileChunk", chunks[i]);
// 发送切片上传请求
await fetch("/update", {
method: "POST",
body: formData,
});
}
};
// 创建接收消息通知
this.addEventListener("message", async (event) => {
const chunkSize = 1 * 1024 * 1024; // 1M
const file = event.data.payload.file;
// 切片的内容存放
const chunks = [];
// 切片的位置
let offset = 0;
while (offset < file.size) {
// 分割切片位置以及每次切片的大小
const chunk = file.slice(offset, offset + chunkSize);
chunks.push(chunk);
// 切片的位置计算
offset = offset + chunkSize;
}
// 上传切片
await uploadChunks(chunks);
// 通知完成
postMessage({ type: "uploadComplete" });
});
主页面
import React, { useEffect } from "react";
const Login: React.FC = () => {
const click = () => {
document.getElementById("upload")?.click();
};
useEffect(() => {
// 创建Worker的实例,并启动实例
const worker = new Worker("src/pages/login/worker.ts");
document.getElementById("upload")?.addEventListener("change", (e) => {
// 获得当前上传的文件
const files = (e.target as HTMLInputElement).files?.[0];
// 接收消息
worker.addEventListener("message", (event) => {
const { type } = event.data;
if (type === "uploadComplete") {
console.log("文件上传完成!");
}
});
if (files) {
// 向worker发送消息
worker.postMessage({ type: "upload", payload: { file: files } });
}
});
}, []);
return (
<div className="login">
<h1>大文件上传</h1>
<button onClick={click}> 选择上传文件</button>
<input type="file" id="upload" style={{ display: "none" }} />
</div>
);
};
export default Login;
完成配合使用webWorker