Files
fe_service_ebtp_frontend/src/components/Upload/react-resumable.js
2021-01-16 11:29:42 +08:00

398 lines
15 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React from "react";
import { Table, Divider, Space, Tag, Progress, Button, Row, Col, Modal, Steps } from 'antd';
import { PauseOutlined, DeleteOutlined, CaretRightOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
import Resumablejs from "./resumable";
import './style.css';
/**
* 大文件上传组件
*/
export default class ReactResumableJs extends React.Component {
constructor(props) {
super(props);
this.state = {
fileList: [],// 任务队列
isPaused: false,// 暂停上传
isUploading: false,// 正在上传
isSupported: false,// 浏览器支持H5分片接口
task: {// 当前执行的任务
id: 0,// 任务id
prograss: 0,// 任务进度
},
timer: 0,// 计时器
};
this.resumable = null;
this.timerInterval;
}
componentDidMount = () => {
// 实例化上传组件( 用的是开源组件 resumablejs )
let _resumableField = this.__createResumbleObj();
// 当前浏览器是否支持 H5 Api
this.setState({ isSupported: _resumableField.support });
// 绑定上传组件, 拖拽组件
_resumableField.assignBrowse(this.uploader);
_resumableField.assignDrop(this.dropZone);
/* 注册新增文件通知 */
_resumableField.on('fileAdded', this.__fileAddedListener.bind(this));
/* 注册上传处理中文件通知 */
_resumableField.on('fileProgress', this.__fileProgressListener.bind(this));
/** 文件上传完成 */
_resumableField.on('fileSuccess', this.__fileSuccessListener.bind(this));
/** 文件上传失败 */
_resumableField.on('fileError', this.__fileErrorListener.bind(this));
this.resumable = _resumableField;
};
render() {
// AntDesign Step组件 用于指示业务运行阶段
const { Step } = Steps;
const columns = [
{
title: '描述',
dataIndex: 'filename',
key: 'filename',
render: text => <b>{text}</b>,
},
{
title: '进度',
dataIndex: 'prograss',
key: 'prograss',
width: '40%',
responsive: ['lg'],
render: (val, record) => <Progress percent={val} status={record.error ? "exception" : "active"} />,
},
{
title: '动作',
dataIndex: 'action',
key: 'action',
width: '20%',
responsive: ['md'],
render: (text, record) => {
if (record.uploading) {
return (
<Space size="middle">
<Button type="dashed" style={{ display: !record.pause && !record.complete && !record.error ? '' : 'none' }} icon={<PauseOutlined />} onClick={() => this.__uploadPause(record.filename)}>暂停</Button>
<Button type="dashed" style={{ display: record.pause && !record.complete && !record.error ? '' : 'none' }} icon={<CaretRightOutlined />} onClick={() => this.__uploadResum(record.filename)}>恢复</Button>
<Button type="danger" style={{ display: record.pause || record.error ? '' : 'none' }} icon={<DeleteOutlined />} onClick={() => this.__removeFile(record.filename)}>移除</Button>
</Space>
);
}
},
},
];
return (
<>
<Row>
<Col span={24}>
<div id={this.props.dropTargetID} ref={node => this.dropZone = node} style={{ display: this.state.fileList.length === 0 ? '' : 'none' }}>
<div className="resumable-error" style={{ display: this.state.isSupported ? 'none' : '' }}>
Your browser, unfortunately, is not supported by Resumable.js. The library requires support for <a href="http://www.w3.org/TR/FileAPI/">the HTML5 File API</a> along with <a href="http://www.w3.org/TR/FileAPI/#normalization-of-params">file slicing</a>.
</div>
<div className="resumable-drop" style={{ display: this.state.isSupported ? '' : 'none' }}>
文件拖拽到该区域或者
<a className="resumable-browse"
ref={node => { this.uploader = node; this.dropZone = node; }}
type="file"
id={this.props.uploaderID}
className='btn'
name={this.props.uploaderID + '-upload'}
accept={this.props.fileAccept || '*'}
disabled={this.props.disableInput || false}><u>点击链接添加文件</u></a>
</div>
</div>
</Col>
</Row>
<Row>
<Col span={24}>
<center>
<h1>总体作业已用时</h1>
<h2>{(() => {
let _timer = this.state.timer;
let _hour = parseInt(_timer / 3600);
let _minute = parseInt(_timer % 3600 / 60);
let _second = parseInt(_timer % 3600 % 60);
return this.__format(_hour) + ":" + this.__format(_minute) + ":" + this.__format(_second);
})()}</h2>
</center>
</Col>
<Col span={24} style={{ paddingTop: '5px' }}>
<Steps current={this.state.task.id} percent={this.state.task.prograss}>
<Step title="招标文件" description="上传招标文件." />
<Step title="文件解密" description="文件解密处理." />
<Step title="任务完成" description="业务完成." />
</Steps>
</Col>
</Row>
<Divider dashed />
<Row>
<Col span={24}>
<Table columns={columns} dataSource={[...this.state.fileList]} pagination={false} bordered />
</Col>
</Row>
</>
);
}
/**
* 格式化计时器
* @param {*} value
*/
__format(value) {
if (value < 10) {
return '0' + value;
}
return value;
}
/**
* 实例化上传组件
*/
__createResumbleObj() {
return new Resumablejs({
target: this.props.service,
query: this.props.query,// 自定义参数
forceChunkSize: true,// 是否严格执行分片
fileType: this.props.filetypes,
maxFiles: this.props.maxFiles,
maxFileSize: this.props.maxFileSize,
headers: this.props.headerObject || {},
chunkSize: this.props.chunkSize,
testChunks: false,// 不启用测试块
simultaneousUploads: this.props.simultaneousUploads,
fileParameterName: this.props.fileParameterName,
forceChunkSize: true,// 是否严格执行分片
maxChunkRetries: 10,// 最大重试次数
chunkRetryInterval: 1000,// 重试间隔时间 10 秒
throttleProgressCallbacks: 1,// 上传进度播报周期 (1秒)
forceChunkSize: this.props.forceChunkSize
});
}
/**
* 添加文件时的监听
*
* @param {*} file
*/
__fileAddedListener(file) {
// 更新文件队列表单
let _cache = this.state.fileList;
_cache.length = 0;
_cache.push({
key: _cache.length + 1,
filename: file.fileName,
uploading: true,
prograss: Math.floor(file.progress() * 100),
});
// 更新组件状态,进行组件重绘
this.setState({ fileList: _cache });
if (typeof this.props.onFileAdded === "function") {
this.props.onFileAdded(file, this.resumable);
} else {
this.resumable.upload();
}
this.timerInterval = setInterval(() => {
this.setState({ timer: this.state.timer + 1 });
}, 1000);
}
/**
* 文件上传进度播报
* @param {*} file
*/
__fileProgressListener(file) {
// 更新文件队列表单
let _cache = this.state.fileList;
let matchedObj = _cache.find((obj) => obj.filename === file.fileName);
if (matchedObj) {
matchedObj.prograss = Math.floor(file.progress() * 100);
// 更新组件状态,进行组件重绘
this.setState({ fileList: _cache, task: { id: 0, process: matchedObj.prograss } });
}
}
/**
* 文件上传完成
* @param {*} file
*/
__fileSuccessListener(file) {
let _cache = this.state.fileList;
let matchedObj = _cache.find((obj) => obj.filename === file.fileName);
if (matchedObj) {
matchedObj.complete = true;
this.setState({ fileList: _cache });// 更新组件状态,进行组件重绘
}
if (typeof this.props.onUploadSuccess === "function") {
_cache.length = 0;// 清空已完成的任务条目
this.props.onUploadSuccess(file, _cache, (index, description, percent) => {
let obj = _cache[index];
if (obj) {
obj.prograss = Math.floor(percent);
obj.filename = description;
this.setState({ fileList: _cache, task: { id: 1, prograss: obj.prograss } });
}
// console.log(obj.prograss)
// alert(obj.prograss)
// 确认是否所有任务都已完成
if (obj.prograss >= 100) {
// this.setState({ fileList: [], task: { id: 2, prograss: 100 } });
this.setState({ fileList: [], task: { id: 0, prograss: 0 } });
this.setState({ timer: 0 });
clearInterval(this.timerInterval);
} else if (obj.prograss == 2) {
this.setState({ fileList: [], task: { id: 0, prograss: 0 } });
this.setState({ timer: 0 });
clearInterval(this.timerInterval);
} else {
this.setState({ fileList: _cache, task: { id: 1, prograss: obj.prograss } });
}
});
}
}
/**
* 文件上传失败
* @param {*} file
*/
__fileErrorListener(file) {
// 更新文件队列表单
let _cache = this.state.fileList;
let matchedObj = _cache.find((obj) => obj.filename === file.fileName);
if (matchedObj) {
matchedObj.error = true;
// 更新组件状态,进行组件重绘
this.setState({ fileList: _cache });
}
if (typeof this.props.onUploadError === "function") {
this.props.onUploadError(file, this.resumable);
}
}
/**
* 移除上传列表中的文件
*/
__removeFile(filename) {
/** 查询当前文件 */
const file = this.resumable.files.find(obj => obj.file.name === filename);
const selfRef = this;
if (!file.isComplete()) {
const { confirm } = Modal;
confirm({
title: '确定要移除当前任务吗?',
icon: <ExclamationCircleOutlined />,
content: '当前上传任务尚未完成,移除后将无法恢复,确定要执行该操作吗?',
okText: '确定',
okType: 'danger',
cancelText: '取消',
onOk() {// 取消选中文件的上传操作
file.cancel();
selfRef.__handleRemoveFile(filename);
},
});
} else {
selfRef.__handleRemoveFile(filename);
}
}
__handleRemoveFile(filename) {
// 更新文件队列表单
let _cache = this.state.fileList;
// 移除指定的元素
_cache.splice(_cache.findIndex((obj) => obj.filename === filename), 1)
// 修改上传列表状态
this.setState({ fileList: _cache });
}
/**
* 暂停上传
*/
__uploadPause(filename) {
// 取消选中文件的上传操作
this.resumable.files.find(obj => obj.file.name === filename)._pause = true;
// 更新文件队列表单
let _cache = this.state.fileList;
let matchedObj = _cache.find((obj) => obj.filename === filename);
if (matchedObj) {
matchedObj.pause = true;
// 更新组件状态,进行组件重绘
this.setState({ fileList: _cache });
}
}
/**
* 恢复上传
*/
__uploadResum(filename) {
let file = this.resumable.files.find(obj => obj.file.name === filename);
// 取消暂停状态
file._pause = false;
// 取消选中文件的上传操作
file.upload();
// 更新文件队列表单
let _cache = this.state.fileList;
let matchedObj = _cache.find((obj) => obj.filename === filename);
if (matchedObj) {
delete matchedObj.pause;
// 更新组件状态,进行组件重绘
this.setState({ fileList: _cache });
}
}
}
/**
* 对外暴露的属性值
*/
ReactResumableJs.defaultProps = {
maxFiles: 10,// 添加最大文件数
uploaderID: 'default-resumable-uploader',
dropTargetID: 'dropTarget',
query: {},
fileAccept: "*",// 文件头类型
maxFileSize: 1204 * 1024 * 1024 * 1024,// max size 1T
onUploadErrorCallback: (file, errorCount) => { },
onFileRemoved: file => file,
onCancelUpload: () => true,
onPauseUpload: () => true,
onResumeUpload: () => true,
onStartUpload: () => true,
onUploadSuccess: () => true,
onUploadError: () => true,
disableDragAndDrop: false,// 允许拖拽操作
chunkSize: 5 * 1024 * 1024,// 每个分批那的尺寸 5M
simultaneousUploads: 5,// 同步上传的分片数
fileParameterName: 'file',
generateUniqueIdentifier: null,
maxFilesErrorCallback: null,
cancelButton: false,
pause: false,
startButton: null,
pauseButton: null,
previousText: "",
headerObject: {},
forceChunkSize: false
};