//WebUploader hook

var chunkSize = 10 * 1024 * 1024;//分片上传,每片5M,默认是5M

var that = this;   //保存this指针

WebUploader.Uploader.register({

name:'my-uploader',

'before-send-file': 'beforeSendFile',

'before-send': 'beforeSend'

}, {

beforeSendFile: function (file) {

// console.log("beforeSendFile");

// Deferred对象在钩子回掉函数中经常要用到,用来处理需要等待的异步操作。

var task = new $.Deferred();

// 根据文件内容来查询MD5

uploader.md5File(file,0,chunkSize).progress(function (percentage) {})

.then(function (val) { // md5计算完成

// console.log('md5 result:', val);

file.md5 = val;

file.uid = WebUploader.Base.guid();

// 进行md5判断

$.post("后端checkMd5的url", {uid: file.uid, md5: file.md5, fileName:file.name},

function (data) {

// console.log(data,'md5 res');

if(data.code=='500'){

message.error(data.msg)

let updateFileList = that.state.fileQueuedList;   //更新文件状态,所有选择的文件保存在fileQueuedList中

let res = updateFileList.map(item=>{

if(item.fileId === file.id){

item.status = "ERROR";

item.statusName = "错误";

}

return item

})

that.setState({

fileQueuedList:res,

})

task.reject(); //遇到不符合要求的文件调用reject方法,可以上传后面正常的文件

}else{

var status = data.status.value;

task.resolve();

if (status == 101) {

// 文件不存在,那就正常流程

}else if (status == 100) {

// 文件存在 忽略上传过程,直接标识上传成功;

message.error(file.name+data.msg);

uploader.skipFile(file);

file.pass = true;

}else if (status == 102) {

// 部分已经上传到服务器了,但是差几个模块。

file.missChunks = data.data;

}

}

}

);

});

return $.when(task);

},

beforeSend: function (block) {

var task = new $.Deferred();

var file = block.file;

var missChunks = file.missChunks;

var blockChunk = block.chunk;

// console.log("当前分块:" + blockChunk);

// console.log("missChunks:" + missChunks);

if (missChunks !== null && missChunks !== undefined && missChunks !== '') {

var flag = true;

for (var i = 0; i < missChunks.length; i++) {

if (blockChunk == missChunks[i]) {

// console.log(file.name + ":" + blockChunk + ":还没上传,现在上传去吧。");

flag = false;

break;

}

}

if (flag) {

task.reject();

} else {

task.resolve();

}

} else {

task.resolve();

}

return $.when(task);

}

});

// 实例化

var uploader = WebUploader.create({

pick: {

id:'#picker',

multiple:true

},

formData: {

uid: 0,

md5: '',

chunkSize: chunkSize,

},

swf: '../webUploader/Uploader.swf', // swf文件路径

chunked: true, //是否要分片处理大文件上传

chunkSize: chunkSize,

threads: 3, //上传并发数。允许同时最大上传进程数。

server: '/dynamic/video/fileUpload', // 文件接收服务端。

auto: false,

duplicate:false,

withCredentials:true,

// accept: {

//   extensions: 'avi,asf,avs,mpg,mov,mp4,m4a,3gp,ogg,flv,ps,ts,dav,rmvb,SV4,SV5,SSDV',

// },

// 禁掉全局的拖拽功能。这样不会出现图片拖进页面的时候,把图片打开。

disableGlobalDnd: true,

// fileNumLimit: 1024, //验证文件总数量, 超出则不允许加入队列。

// fileSizeLimit: 1024 * 1024 * 1024, // 1G 验证文件总大小是否超出限制, 超出则不允许加入队列。

// fileSingleSizeLimit: 20*1024 * 1024 * 1024 // 20G 验证单个文件大小是否超出限制, 超出则不允许加入队列。

});

that.setState({      //把实例保存到state中

uploader:uploader

})

// 当有文件被添加进队列的时候

uploader.on('fileQueued', function (file) {

let appendFile = that.state.fileQueuedList;

let res = appendFile.some(item=>{

return item.file.name==file.name

})

if(res){

// message.error(file.name+'文件重复。')

return

}

appendFile.push({

file:file,    //把file对象也保存下来

fileId:file.id,

progress:'0%',

status:'START',

statusName:'待开始',

})

that.setState({

fileQueuedList:appendFile,

})

});

//当某个文件的分块在发送前触发,主要用来询问是否要添加附带参数,大文件在开起分片上传的前提下此事件可能会触发多次。

uploader.onUploadBeforeSend = function (obj, data) {

// console.log("onUploadBeforeSend");

var file = obj.file;

data.md5 = file.md5 || '';

data.uid = file.uid;

};

// 上传中

uploader.on('uploadProgress', function (file, percentage) {

let updateFileList = that.state.fileQueuedList;

let res = updateFileList.map(item=>{     //文件上传中时更新文件状态和进度条

if(item.fileId === file.id){

item.progress=Math.floor(percentage * 100) + '%';

item.status = "UPLOADING";

item.statusName = "上传中";

}

return item

})

that.setState({

fileQueuedList:res,

})

// console.log(Math.floor(percentage * 100) + '%',file.name,'上传进度')

});

// 上传返回结果

uploader.on('uploadSuccess', function (file) {

// console.log('success')

let updateFileList = that.state.fileQueuedList;

let res = updateFileList.map(item=>{    //文件上传成功更新状态

if(item.fileId === file.id){

item.progress='100%';

item.status = "UPLOADED";

item.statusName = "已完成"

}

return item

})

//判断是不是都上传完,可以将该判断放在uploadComplete函数中,uploadSuccess只监听的到已成功的文件,uploadComplete函数无论成功失败都可以监听到

let isAllCompleted = updateFileList.every(item=>{

return item.status==="UPLOADED"||item.status==="ERROR"

})

that.setState({

fileQueuedList:res,

isAllCompleted:isAllCompleted

})

if(isAllCompleted){ //都上传成功之后

that.props.onClose&&that.props.onClose() //关闭弹窗

that.props.getFileList&&that.props.getFileList() //刷新文件table

}

});

uploader.on('error', function (type,file) {

// message.error("上传出错!请检查后重新上传!错误代码"+type);

// if(type=='F_DUPLICATE'){

// message.error(file.name+'文件重复')

// }

// if (type == "Q_TYPE_DENIED") {

// message.error("请上传视频格式文件");

// }else {

// message.error("上传出错!请检查后重新上传!错误代码"+type);

// }

});

}

//点击文件的"开始"Icon,obj为当前点击的文件对象,即currentItem in fileQueuedList

fileUpload(obj){

const {uploader,fileQueuedList} = this.state;

uploader.upload(obj.file)

let updateObj = fileQueuedList;

let idx = fileQueuedList.indexOf(obj);

updateObj[idx].status = "UPLOADING";

updateObj[idx].statusName = "上传中";

this.setState({fileQueuedList:updateObj})

}

//点击暂停Icon

fileStop(obj){

const {uploader,fileQueuedList} = this.state;

uploader.cancelFile(obj.file)

//此处为第一个坑,在API里暂停是调用stop方法,此处想要暂停指定文件,显然应该用stop(file)方法,

然而实践之后发现调用stop(file)方法会报错 “Cannot read property 'file' of undefined”,

之后再点击继续发现无法继续上传,没有发出请求。

后来经过各种尝试后采用了cancelFile方法,可以暂停并继续,但此方法会标记文件为已取消状态,可以再次手动选择添加进队列,从而不触发文件重复的error监听。

let idx = fileQueuedList.indexOf(obj);

let updateObj = fileQueuedList;

updateObj[idx].status = "PAUSE";

updateObj[idx].statusName = "已暂停";

this.setState({fileQueuedList:updateObj})

}

//文件暂停时点击继续开始Icon

fileContinue(obj){

const {uploader,fileQueuedList} = this.state;

uploader.retry(obj.file)  //继续上传可以采用retry方法也可以使用upload方法

let idx = fileQueuedList.indexOf(obj);

let updateObj = fileQueuedList;

updateObj[idx].status = "UPLOADING";

updateObj[idx].statusName = "上传中";

this.setState({fileQueuedList:updateObj})  //更新文件状态

}

//点击文件删除Icon

clickDeleteIcon(obj){

let that = this;

const {uploader,fileQueuedList} = that.state;

let updateObj = fileQueuedList;

let idx = fileQueuedList.indexOf(obj);

updateObj.splice(idx,1)

uploader.cancelFile(obj.file);

that.setState({fileQueuedList:updateObj})

}

//点击开始上传按钮

startUpload(){

const{uploader,fileQueuedList} = this.state;

let PausedFile = fileQueuedList.filter(item=>{

return item.status==="PAUSE"

})

// console.log(PausedFile)

if(PausedFile&&PausedFile.length>0){    //如果有已暂停的文件则从已暂停的文件中第一个开始上传

uploader.upload(PausedFile[0].file)

}else{

uploader.upload()

}

}

//弹窗关闭

onClose(){

const {fileQueuedList,isAllCompleted,uploader} = this.state;

if(!isAllCompleted){

let res = fileQueuedList&&fileQueuedList.reduce((data,current)=>{  //把除了错误和上传完成的文件暂停

if(current.status!=='UPLOADED'||current.status!=='ERROR'){

current.status="PAUSE";

current.statusName="已暂停";

uploader.stop(true);

data.push(current)

}

return data

},[])

// console.log(res,'res')

this.props.saveFileStatus&&this.props.saveFileStatus(res)  //把所有添加的文件状态保存下来传给父组件。再有父组件通过props传给子组件

}

this.props.onClose&&this.props.onClose()

this.props.getFileList()

}

componentDidMount(){

//挂载完成后获取父组件的props保存的文件状态

const {savedFileList} = that.props;  //savedFileList保存了关闭弹窗后未上传完的任务列表

// console.log(savedFileList,'saved')

this.uploadOperate()  //把WebUploader相关的代码统一写在了此函数中,挂载时调用,注册hook并生成WebUploader实例

if(savedFileList&&savedFileList.length>0){

this.setState({

fileQueuedList:savedFileList,    //赋值,显示未完成的文件列表

},()=>{

const {uploader,fileQueuedList} = that.state;

let files = fileQueuedList.map(item=>{

return item.file

})

for(let i = 0; i < files.length;i++){

uploader.removeFile(files[i],true)

}

uploader.addFiles(files)

//遍历所有的未完成任务,移除任务后再重新添加,目的是这样会触发fileQueue事件,否则进来点继续上传只会触发uploadProgress函数,在这个函数里有setState方法,但是会报错“Warning: setState(...): Can only update a mounted or mounting component. This usually means you called setState() on an unmounted component.” 发现上传请求是正常进行的,但是页面进度条不渲染,这也是第二个坑点,博主当时也没有找到原因,因为componentDidMount函数已经触发了,uploader实例也生成了,为什么还是unmounted component呢?于是便各种尝试,最终衍生出了上述代码,解决了这个进度条不渲染的,需求到此也是都实现了。。。

})

}

}

}

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐