通過 antd 框架的 Upload 控制項,採用手動上傳的方式,先選擇需要上傳的文件(控制文件數量以及大小),再根據所選的文件列表,迴圈上傳,期間通過 Spin 控制項提示上傳中。 ...
基本步驟
通過 antd 框架的 Upload 控制項,採用手動上傳的方式,先選擇需要上傳的文件(控制文件數量以及大小),再根據所選的文件列表,迴圈上傳,期間通過 Spin 控制項提示上傳中。
效果展示
控制項引用
Upload 控制項配置:
1 props : { 2 multiple: true, 3 maxCount:20,//限制最多顯示 20 個文件 4 onRemove: (file) => {//刪除列表文件 5 let fileListbatch_curr=this.state.fileListbatch; 6 console.log("props-onRemove-fileListbatch_curr:",fileListbatch_curr); 7 let index = fileListbatch_curr.findIndex(item=>item.uid==file.uid); 8 console.log("props-onRemove-obj:",index,file.uid); 9 if(index==-1){ 10 //message.warning("未刪除成功!") 11 return; 12 }else{ 13 const newFileList = fileListbatch_curr.slice(); 14 newFileList.splice(index, 1); 15 console.log("props-onRemove-newFileList:",newFileList); 16 this.setState({fileListbatch:newFileList}); 17 let uploadsuccesslist_curr=this.state.uploadsuccesslist; 18 let indexsuccess=uploadsuccesslist_curr.findIndex(item=>item.uid==file.uid);//根據唯一碼 uid 查找目標文件的索引 19 if(indexsuccess!=-1){ 20 uploadsuccesslist_curr.splice(indexsuccess, 1);//刪除 21 console.log("props-onRemove-uploadsuccesslist_curr:",uploadsuccesslist_curr); 22 this.setState({uploadsuccesslist:uploadsuccesslist_curr}); 23 } 24 } 25 }, 26 beforeUpload: (file) => {//添加文件,將文件加入臨時列表,準備上傳 27 let fileListbatch_curr=this.state.fileListbatch; 28 let ff=fileListbatch_curr.find((item)=>item.name==file.name); 29 if(ff==undefined){ 30 fileListbatch_curr.push(file); 31 this.setState({fileListbatch:fileListbatch_curr,uploadflag:false}); 32 return false; 33 } 34 else{ 35 message.warning("存在同名文件已選擇,請確認!"); 36 return Upload.LIST_IGNORE;//列表中不顯示 37 } 38 }, 39 fileListbatch_cc, 40 }
界面元素排列:
1 <Upload {...props}> 2 <Button icon={<UploadOutlined />}>選擇文件(Max:20Pcs, Max:200MB)</Button> 3 </Upload> 4 <Button 5 type="primary" 6 onClick={this.batchUploadReports} 7 disabled={fileListbatch.length === 0} 8 //loading={uploading} 9 style={{ marginTop: 20,width:180 }} 10 > 11 {/* {uploading ? 'Uploading' : 'Start Upload'} */} 12 開始上傳 13 </Button> 14 <label style={{ lineHeight: "0px", color: '#bfbfbf', fontSize: 10,float:'left',marginBottom:20 }}>支持擴展名:apk/exe/pdf/xls/doc/ppt等</label>
處理邏輯
根據文件列表,迴圈上傳全部文件。
【後端採用 .Net 5.0 WebAPI 詳見:大文件分片上傳 中的“後端部分”】
1 //批量上傳文件 2 batchUploadReports = () => { 3 this.setState({uploadsuccesslist:[]}) 4 let uploadsuccesslist_curr=[]; 5 this.formRef_upload.current.validateFields() 6 .then(formrefcurr => { 7 if(formrefcurr["baogaolb"]==0||formrefcurr["baogaolx"].length==0){ 8 message.warning("請檢查必填項!"); 9 return null; 10 } 11 this.setState({fileuploading:true,uploadsuccesslist:[],tipContent:"報告文件上傳中,請耐心等待..."}); 12 let fileListbatch_curr2=this.state.fileListbatch; 13 let filecount=fileListbatch_curr2.length; 14 let filecount_success=0; 15 if(filecount>20){//只取前 20 個文件 16 filecount=20; 17 fileListbatch_curr2=fileListbatch_curr2.splice(0,20); 18 } 19 try{//通過拋出異常,來中斷 foreach 20 fileListbatch_curr2.forEach(element => {//文件 list 迴圈上傳 21 if(element.size/(1024*1024)>200){ 22 this.setState({fileuploading:false,uploadsuccesslist:[],tipContent:"報告文件上傳中,請耐心等待..."}) 23 message.warning("單個報告大小不允許超過 200MB,請檢查後繼續上傳!"); 24 // fileListbatch_curr2.length=0; 25 throw new Error(element)//若有不符合條件的文件,拋出異常中斷迴圈 26 } 27 else{ 28 let filename=element.name; 29 let chunklistcurr=this.createFileChunk(element).map(({ file, name }, index) => {//createFileChunk:創建文件切片,可處理大文件 30 return { 31 chunk: file, 32 size: file.size, 33 percent: 0, 34 //filename:file. 35 name: name,// + "-" + (index + 1), 36 index, 37 }; 38 }); 39 let filecountchunk=chunklistcurr.length; 40 let ii=0; 41 chunklistcurr.forEach(element => {//分片傳輸 42 const formData = new FormData() 43 formData.append('file', element.chunk); 44 formData.append('index', element.index); 45 formData.append('name', element.name); 46 formData.append('size', element.size); 47 //formData.append('filecount', filecount); 48 axios({ 49 method: 'post', 50 url: '/api/system/System/UploadFileAttachmentChunk?zhuti='+this.state.zhuti, 51 data: formData, 52 headers: { "Content-Type": "multipart/form-data"} 53 }).then(({data}) => { 54 if(data.code==200){ 55 ii++; 56 if(ii==filecountchunk){//分塊全部上傳完成 57 let indata={"name":chunklistcurr[0].name,"filecount":filecountchunk,"filename":filename 58 } 59 axios({//傳輸完成,通知拼接 60 method: 'post', 61 url: '/api/system/System/CombineChunkToFileBatch', 62 data: indata, 63 headers: { "Content-Type": "application/json"} 64 }).then(({data}) => { 65 if(data.code==200){ 66 let listbatchupload={} 67 uploadsuccesslist_curr.push(listbatchupload); 68 this.setState({uploadsuccesslist:uploadsuccesslist_curr}); 69 filecount_success++; 70 } 71 else if(data.code==202){ 72 window.location.href="/wellcome"; 73 } 74 else{ 75 message.error("上傳失敗,請稍後重試!詳情:"+data.desc); 76 filecount_success++; 77 } 78 }).catch((err) =>{ 79 console.log(err); 80 message.error("上傳失敗,請稍後重試!"); 81 }).finally(() =>{ 82 if(filecount_success==filecount){ 83 this.setState({uploadflag:true}) 84 message.success("全部上傳完成,請進一步確認上傳是否全部成功"); 85 this.setState({fileuploading:false,tipContent:"載入中..."}); 86 } 87 }) 88 } 89 } 90 else if(data.code==202){ 91 window.location.href="/wellcome"; 92 } 93 else{ 94 message.error("上傳失敗,請稍後重試!詳情:"+data.desc); 95 chunklistcurr.length=0; 96 return; 97 } 98 }).catch((err) =>{ 99 console.log(err); 100 message.error("上傳失敗,請稍後重試!"); 101 return; 102 }).finally(() =>{ }) 103 }) 104 } 105 }); 106 } 107 catch(e){ 108 console.log('catch-e:'+e) 109 } 110 }) 111 }
1 //創建文件切片 2 createFileChunk = (file, size = 20*1024*1024) => { 3 const fileChunkList = []; 4 let cur = 0; 5 while (cur < file.size) { 6 fileChunkList.push({ file: file.slice(cur, cur + size), name: file.uid });//uid:文件唯一標識 7 cur += size; 8 } 9 return fileChunkList; 10 };
註:本文代碼已在項目中實用,有疑問歡迎指正。