2020-12-23 11:14:35 +08:00
import React from "react" ;
2021-01-16 11:29:42 +08:00
import { Table , Divider , Space , Tag , Progress , Button , Row , Col , Modal , Steps } from 'antd' ;
2020-12-23 11:14:35 +08:00
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 = {
2021-01-16 11:29:42 +08:00
fileList : [ ] , // 任务队列
isPaused : false , // 暂停上传
isUploading : false , // 正在上传
isSupported : false , // 浏览器支持H5分片接口
task : { // 当前执行的任务
id : 0 , // 任务id
prograss : 0 , // 任务进度
} ,
timer : 0 , // 计时器
2020-12-23 11:14:35 +08:00
} ;
this . resumable = null ;
2021-01-16 11:29:42 +08:00
this . timerInterval ;
2020-12-23 11:14:35 +08:00
}
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 ( ) {
2021-01-16 11:29:42 +08:00
// AntDesign Step组件, 用于指示业务运行阶段
const { Step } = Steps ;
2020-12-23 11:14:35 +08:00
const columns = [
{
2021-01-16 11:29:42 +08:00
title : '描述' ,
dataIndex : 'filename' ,
key : 'filename' ,
render : text => < b > { text } < / b > ,
2020-12-23 11:14:35 +08:00
} ,
{
2021-01-16 11:29:42 +08:00
title : '进度' ,
dataIndex : 'prograss' ,
key : 'prograss' ,
width : '40%' ,
responsive : [ 'lg' ] ,
render : ( val , record ) => < Progress percent = { val } status = { record . error ? "exception" : "active" } / > ,
2020-12-23 11:14:35 +08:00
} ,
{
2021-01-16 11:29:42 +08:00
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 ) } > 暂停 < / B u t t o n >
< Button type = "dashed" style = { { display : record . pause && ! record . complete && ! record . error ? '' : 'none' } } icon = { < CaretRightOutlined / > } onClick = { ( ) => this . _ _uploadResum ( record . filename ) } > 恢复 < / B u t t o n >
< Button type = "danger" style = { { display : record . pause || record . error ? '' : 'none' } } icon = { < DeleteOutlined / > } onClick = { ( ) => this . _ _removeFile ( record . filename ) } > 移除 < / B u t t o n >
< / S p a c e >
) ;
}
} ,
2020-12-23 11:14:35 +08:00
} ,
2021-01-16 11:29:42 +08:00
] ;
2020-12-23 11:14:35 +08:00
return (
< >
< Row >
< Col span = { 24 } >
2021-01-16 11:29:42 +08:00
< 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 > .
2020-12-23 11:14:35 +08:00
< / d i v >
2021-01-16 11:29:42 +08:00
< 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 >
2020-12-23 11:14:35 +08:00
< / d i v >
< / d i v >
< / C o l >
< / R o w >
< Row >
< Col span = { 24 } >
2021-01-16 11:29:42 +08:00
< center >
< h1 > 总体作业已用时 < / h 1 >
< 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 ) ;
} ) ( ) } < / h 2 >
< / c e n t e r >
< / C o l >
< 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 = "业务完成." / >
< / S t e p s >
2020-12-23 11:14:35 +08:00
< / C o l >
< / R o w >
2021-01-16 11:29:42 +08:00
< Divider dashed / >
< Row >
< Col span = { 24 } >
< Table columns = { columns } dataSource = { [ ... this . state . fileList ] } pagination = { false } bordered / >
< / C o l >
< / R o w >
2020-12-23 11:14:35 +08:00
< / >
) ;
}
2021-01-16 11:29:42 +08:00
/ * *
* 格式化计时器
* @ param { * } value
* /
_ _format ( value ) {
if ( value < 10 ) {
return '0' + value ;
}
return value ;
}
2020-12-23 11:14:35 +08:00
/ * *
* 实例化上传组件
* /
_ _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 ) {
// 更新文件队列表单
2021-01-16 11:29:42 +08:00
let _cache = this . state . fileList ;
_cache . length = 0 ;
2020-12-23 11:14:35 +08:00
_cache . push ( {
key : _cache . length + 1 ,
filename : file . fileName ,
uploading : true ,
2021-01-16 11:29:42 +08:00
prograss : Math . floor ( file . progress ( ) * 100 ) ,
2020-12-23 11:14:35 +08:00
} ) ;
// 更新组件状态,进行组件重绘
this . setState ( { fileList : _cache } ) ;
if ( typeof this . props . onFileAdded === "function" ) {
this . props . onFileAdded ( file , this . resumable ) ;
} else {
this . resumable . upload ( ) ;
}
2021-01-16 11:29:42 +08:00
this . timerInterval = setInterval ( ( ) => {
this . setState ( { timer : this . state . timer + 1 } ) ;
} , 1000 ) ;
2020-12-23 11:14:35 +08:00
}
/ * *
* 文件上传进度播报
* @ param { * } file
* /
_ _fileProgressListener ( file ) {
// 更新文件队列表单
let _cache = this . state . fileList ;
let matchedObj = _cache . find ( ( obj ) => obj . filename === file . fileName ) ;
2021-01-16 11:29:42 +08:00
if ( matchedObj ) {
matchedObj . prograss = Math . floor ( file . progress ( ) * 100 ) ;
2020-12-23 11:14:35 +08:00
// 更新组件状态,进行组件重绘
2021-01-16 11:29:42 +08:00
this . setState ( { fileList : _cache , task : { id : 0 , process : matchedObj . prograss } } ) ;
2020-12-23 11:14:35 +08:00
}
}
/ * *
* 文件上传完成
* @ param { * } file
* /
_ _fileSuccessListener ( file ) {
let _cache = this . state . fileList ;
let matchedObj = _cache . find ( ( obj ) => obj . filename === file . fileName ) ;
2021-01-16 11:29:42 +08:00
if ( matchedObj ) {
2020-12-23 11:14:35 +08:00
matchedObj . complete = true ;
2021-01-16 11:29:42 +08:00
this . setState ( { fileList : _cache } ) ; // 更新组件状态,进行组件重绘
2020-12-23 11:14:35 +08:00
}
if ( typeof this . props . onUploadSuccess === "function" ) {
2021-01-16 11:29:42 +08:00
_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 } } ) ;
}
} ) ;
2020-12-23 11:14:35 +08:00
}
}
/ * *
* 文件上传失败
* @ param { * } file
* /
_ _fileErrorListener ( file ) {
// 更新文件队列表单
let _cache = this . state . fileList ;
let matchedObj = _cache . find ( ( obj ) => obj . filename === file . fileName ) ;
2021-01-16 11:29:42 +08:00
if ( matchedObj ) {
2020-12-23 11:14:35 +08:00
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 ;
2021-01-16 11:29:42 +08:00
if ( ! file . isComplete ( ) ) {
2020-12-23 11:14:35 +08:00
const { confirm } = Modal ;
confirm ( {
title : '确定要移除当前任务吗?' ,
icon : < ExclamationCircleOutlined / > ,
content : '当前上传任务尚未完成,移除后将无法恢复,确定要执行该操作吗?' ,
okText : '确定' ,
okType : 'danger' ,
cancelText : '取消' ,
onOk ( ) { // 取消选中文件的上传操作
file . cancel ( ) ;
selfRef . _ _handleRemoveFile ( filename ) ;
} ,
} ) ;
} else {
selfRef . _ _handleRemoveFile ( filename ) ;
}
}
_ _handleRemoveFile ( filename ) {
// 更新文件队列表单
2021-01-16 11:29:42 +08:00
let _cache = this . state . fileList ;
2020-12-23 11:14:35 +08:00
// 移除指定的元素
2021-01-16 11:29:42 +08:00
_cache . splice ( _cache . findIndex ( ( obj ) => obj . filename === filename ) , 1 )
2020-12-23 11:14:35 +08:00
// 修改上传列表状态
this . setState ( { fileList : _cache } ) ;
}
/ * *
* 暂停上传
* /
2021-01-16 11:29:42 +08:00
_ _uploadPause ( filename ) {
2020-12-23 11:14:35 +08:00
// 取消选中文件的上传操作
this . resumable . files . find ( obj => obj . file . name === filename ) . _pause = true ;
// 更新文件队列表单
let _cache = this . state . fileList ;
let matchedObj = _cache . find ( ( obj ) => obj . filename === filename ) ;
2021-01-16 11:29:42 +08:00
if ( matchedObj ) {
2020-12-23 11:14:35 +08:00
matchedObj . pause = true ;
// 更新组件状态,进行组件重绘
this . setState ( { fileList : _cache } ) ;
}
}
/ * *
* 恢复上传
* /
2021-01-16 11:29:42 +08:00
_ _uploadResum ( filename ) {
2020-12-23 11:14:35 +08:00
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 ) ;
2021-01-16 11:29:42 +08:00
if ( matchedObj ) {
2020-12-23 11:14:35 +08:00
delete matchedObj . pause ;
// 更新组件状态,进行组件重绘
this . setState ( { fileList : _cache } ) ;
}
}
}
/ * *
* 对外暴露的属性值
* /
ReactResumableJs . defaultProps = {
maxFiles : 10 , // 添加最大文件数
uploaderID : 'default-resumable-uploader' ,
dropTargetID : 'dropTarget' ,
query : { } ,
2021-01-16 11:29:42 +08:00
fileAccept : "*" , // 文件头类型
2020-12-23 11:14:35 +08:00
maxFileSize : 1204 * 1024 * 1024 * 1024 , // max size 1T
2021-01-16 11:29:42 +08:00
onUploadErrorCallback : ( file , errorCount ) => { } ,
onFileRemoved : file => file ,
onCancelUpload : ( ) => true ,
onPauseUpload : ( ) => true ,
onResumeUpload : ( ) => true ,
onStartUpload : ( ) => true ,
onUploadSuccess : ( ) => true ,
onUploadError : ( ) => true ,
2020-12-23 11:14:35 +08:00
disableDragAndDrop : false , // 允许拖拽操作
chunkSize : 5 * 1024 * 1024 , // 每个分批那的尺寸 5M
simultaneousUploads : 5 , // 同步上传的分片数
fileParameterName : 'file' ,
generateUniqueIdentifier : null ,
maxFilesErrorCallback : null ,
cancelButton : false ,
pause : false ,
startButton : null ,
pauseButton : null ,
previousText : "" ,
2021-01-16 11:29:42 +08:00
headerObject : { } ,
2020-12-23 11:14:35 +08:00
forceChunkSize : false
} ;