前端大文件上传webuploader(react + umi)
使用WebUploader还可以批量上传文件、支持缩略图等等众多参数选项可设置,以及多个事件方法可调用,你可以随心所欲的定制你要的上传组件。
分片上传
1.什么是分片上传
分片上传,就是将所要上传的文件,按照一定的大小,将整个文件分隔成多个数据块(我们称之为Part)来进行分别上传,上传完之后再由服务端对所有上传的文件进行汇总整合成原始的文件。
2.分片上传的场景
1.大文件上传
2.网络环境环境不好,存在需要重传风险的场景
断点续传
1、什么是断点续传
断点续传是在下载或上传时,将下载或上传任务(一个文件或一个压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传或下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传或者下载未完成的部分,而没有必要从头开始上传或者下载。本文的断点续传主要是针对断点上传场景。
2、应用场景
断点续传可以看成是分片上传的一个衍生,因此可以使用分片上传的场景,都可以使用断点续传。
具体使用
1.引入库
使用Web Uploader文件上传需要引入三种资源:JS, CSS, SWF。
先将webuploader.js , webuploader.css , jquery.min.js 放到静态资源public/scripts 文件中如:
由于我是react + Umi 的项目,在config.ts 文件中进行配置,所以导入方式:
links: [
{
href: './scripts/webuploader.css',
rel: 'preload',
},
],
/**
* @name <head> 中额外的 script
* @description 配置 <head> 中额外的 script
*/
headScripts: [
// 解决首次加载时白屏的问题
{ src: '/scripts/loading.js', async: true },
{
src: '/scripts/jquery.min.js',
charset: 'utf-8',
type: 'text/javascript',
},
{
src: '/scripts/webuploader.js',
charset: 'utf-8',
type: 'text/javascript',
},
// `https://sandcastle.cesium.com/Sandcastle-header.js`,
`window.WebUploader = WebUploader`,
],
2.业务组件
index.ts
<div>
<div id="upload-container">
<span>点击或者拖拽到此处上传</span>
</div>
<span id="picker" class="showclass">点击上传文件</span>
{uploading && (
<>
{uploader.current.getFiles()?.[0].name}
<Progress percent={percent} />
</>
)}
</div>
3.初始化
从前端代码可以看出上传文件为一个span文本,Web Uploader将其初始化成为
一个可以上传文件的按钮
import { useRef, useEffect, useState } from "react";
import {
Progress,
} from "antd";
const Index = () => {
const uploader = useRef(null as any);
const [percent, setPercent] = useState(0);
const [errRes, setErrRes] = useState({} as any);
const [uploading, setUploading] = useState(false);
useEffect(() => {
$('#upload-container').click(function(event) {
$("#picker").find('input').click();
});
// 取消上传
$('.upload-list').on('click', '.btn-remove', function(){
// 从文件队列中删除某个文件id
file_id = $(this).data('id');
uploader.current.removeFile(file_id, true); // 从队列中删除
// console.log(uploader.getFiles()) // 队列显示还在 其实已经删除
});
// 重新上传
$('.upload-list').on('click', '.upload-item__progress span', function(){
uploader.current.retry($(this).data('file'));
});
// 刷新容器(解决文件上传按钮失效问题)
$("#picker").hide();
$('#forecastSelect4').change(function(){
$("#picker").show();
uploader.refresh();//刷新容器 解决隐藏后失效问题
});
initUploader();
});
const initUploader = () => {
const { WebUploader } = window as any;
uploader.current = WebUploader.create({
auto: false, // 选完文件后,是否自动上传。
//duplicate:true, // 可重复上传
swf: "/Uploader.swf", // swf文件路径
server: "/api/data/xxx", // 文件接收服务端。
pick: {
id: "#picker",
label: "Select Data File",
multiple: false,
}, // 内部根据当前运行是创建,可能是input元素,也可能是flash. 这里是div的id
// multiple: true, // 选择多个
chunked: true, // 开起分片上传。
threads: 5, // 上传并发数。允许同时最大上传进程数。
method: "POST", // 文件上传方式,POST或者GET。
// fileSizeLimit: 1024 * 1024 * 100 * 100, //验证文件总大小是否超出限制, 超出则不允许加入队列。
// fileSingleSizeLimit: 1024 * 1024 * 100 * 100, //验证单个文件大小是否超出限制, 超出则不允许加入队列。
formData: {},
fileVal: "file", // [默认值:'file'] 设置文件上传域的name。
accept: {
title: "package",
extensions: "tar.gz,tgz,tar,zip",
mimeTypes: ".tar.gz,.tgz,.tar,.zip",
},
headers: {
token: localStorage.getItem("token"),
},
});
uploader.current.on(
"uploadBeforeSend",
async function ({ file }: any, data: any) {
// data.md5 = file.md5; // md5 在父组件中提前一步注册
if (data.hasOwnProperty("chunk")) {
data.chunk = data.chunk + 1;
} else {
data.chunk = 1;
}
}
);
uploader.current.on("beforeFileQueued", async (file: any) => {
uploader.current.reset();
});
// 1.添加文件到队列时
uploader.current.on("fileQueued", async (file: any) => {
form
.validateFields()
.then((values: any) => {
createProjectRequest({
project_name: values.project_name,
sub_project_name: values.sub_project_name,
}).then((res: any) => {
setUploading(true);
fileRef.current = file;
subPid.current = res.sub_project_id;
uploader.current.upload();
});
})
.catch((err: any) => {
uploader.current.reset();
});
});
// 2.文件上传过程中添加进度显示
uploader.current.on("uploadProgress", (file: any, p: number) => {
const _p = Math.floor(p * 100);
setPercent(_p > 99.99 ? 99.99 : _p);
});
// 3.文件上传成功或失败时触发
uploader.current.on("uploadSuccess", (file: any, response: any) => {
completeProjectData({
name: file.name,
size: file.size,
sub_project_id: subPid.current,
})
.then((res) => {
setPercent(100);
message.success("导入成功");
})
.catch(() => {
setPercent(100);
});
});
}
export default Index;