es7提出的async/await概念已經存在有至關長一段時間,具體概念用法就不在這裏贅述了,優點在於處理解決then鏈多層嵌套回調的問題,使得代碼更爲簡單清晰。react
本文在這裏要講的是批量上傳多張圖片時,若是不分批上傳可能觸發瀏覽器的併發限制,亦或是圖片過多過大致使上傳超時,都會影響圖片的上傳成功率。因此,咱們須要分批上傳圖片時,async/await概念就能很好的解決咱們的問題。不然,就只能使用遞歸來處理,或是隻容許單張上傳影響用戶體驗。ajax
其實所有代碼和react沒有太大關係,只是用到部分特性,是能夠適用於任何框架的。至於批量獲取圖片的組件我直接用的是react-dropzone,能夠拖拽圖片,固然了,使用原生的
<input type="file" accept={//圖片類型} multiple/>也是徹底OK的。瀏覽器
預覽代碼併發
//處理獲取的圖片 handleDropFiles = (acceptedFiles) => { const { maxCount, //最多上傳圖片張數 limit } = this.props; let { selectedFilesTotalSize, selectedFiles } = this.state; const _selectedFiles_ = selectedFiles.map(item => item.file); //已經成功獲取過的圖片 const successFiles = [], //獲取成功的圖片 rejectedFiles = [], //獲取失敗的圖片 existFiles = []; //已經存在的圖片 if (acceptedFiles && acceptedFiles.length) { for (const file of acceptedFiles) { if (limit * 1024 < file.size) { rejectedFiles.push(file); } else { const index = _selectedFiles_.findIndex(acceptedFile => this.isSameFile(file, acceptedFile)); //經過文件名文件大小判斷是不是同一文件 if (index >= 0) { existFiles.push(file); } else { successFiles.push(file); } } } } // 如有不符合條件的圖片輸出錯誤信息 let toastMessage = ''; if (existFiles.length) { const existFilesName = existFiles.map(item => `"${item.name}"`); toastMessage = `${existFilesName.join(', ')}等文件已存在;</br>`; } if (rejectedFiles.length) { const rejectedFilesName = rejectedFiles.map(item => `"${item.name}"`); toastMessage = `${toastMessage}${rejectedFilesName.join(', ')}等文件不符合上傳條件;</br>`; } const incrementLength = successFiles.length; const selectedFilesLength = selectedFiles.length; if (incrementLength + selectedFilesLength > maxCount) { const overflowFiles = successFiles.splice(maxCount - selectedFilesLength); const overflowFilesName = overflowFiles.map(item => `"${item.name}"`); toastMessage = `${toastMessage}${overflowFilesName.join(', ')}等文件超出上傳數量的限制;</br>`; } toastMessage && this.props.onError( toastMessage ); // 多圖預覽 若是隻須要用雲服務上傳後的url預覽能夠將此步驟替換爲handleUploadFiles的代碼 if (incrementLength) { // 這裏選擇了createObjectURL而不是readAsDataURL具體區別不詳說了 若是要用readAsDataURL還得Promise.all一下 for (const file of successFiles) { const dataUrl = URL.createObjectURL(file); selectedFiles.push({ file, name: file.name, size: file.size, dataUrl, uploadStatus: 'beforeUpload' //標識圖片狀態,以後有可能上傳失敗須要從新上傳 }); } selectedFiles = selectedFiles.map((item, index) => return {...item, {index: index}}); selectedFilesTotalSize = selectedFiles.reduce((previousSize, nextFile) => previousSize + nextFile.size, 0); this.setState({ selectedFiles, selectedFilesTotalSize }); } }
批量上傳代碼app
// 批量上傳獲取的圖片 handleUploadFiles = async () => { const { batchCount, //一組最多上傳圖片張數(考慮到瀏覽器併發) batchLimit //最多上傳一組圖片大小(考慮到瀏覽器上傳速度限制) } = this.props; const { selectedFiles, uploadedFiles } = this.state; const chunkFiles = chunkFile(selectedFiles.map(file => ['beforeUpload', 'failed'].includes(file.uploadStatus))); //根據batchCount&batchLimit給未上傳或上傳失敗的圖片組分塊 const rate = chunkFiles.length; for (const [index, chunkFile] of chunkFiles.entries()) { toast.show(`圖片上傳中${~~(index+1)/rate*100}%`, 'loading', this.timeout); //這裏作了個假的圖片已上傳率 const uploadFilePromise = chunkFile.map(this.uploadFile); await Promise.all(uploadFilePromise).then((uploadFiles) => { for (const file of uploadFiles) { if ('error' in item) { selectedFiles.find(item => item.index === file.index).uploadStatus = 'failed'; //若上傳失敗更改selectedFiles裏的圖片狀態 } else { uploadedFiles.push({ url: file.url }); } } }); this.setState({ selectedFiles, uploadedFiles }); this.props.onSuccess(uploadedFiles); } toast.hide(); // 分組上傳 function chunkFile(files) { let array = [], subArray = [], size = 0; files.forEach((item, index) => { size += item.size; if (size > batchLimit*1024 || subArray.length === batchCount) { array.push(subArray); subArray = [item]; size = item.size; } else { subArray.push(item); } if (index === files.length-1) { array.push(subArray); } }); return array; } }
上傳圖片框架
// ajax上傳單張圖片,就是簡單的FormData隨便看下就好 uploadFile = (file, index) => { return new Promise((resolve, reject) => { const formData = new FormData(); formData.append('file', file); $.ajax(..., (data)=>{ data = (data && data.data) || {}; resolve({ url: data.url, index }); }, (err) => { //this.props.onError(err && err.errMsg || '上傳失敗'); //能夠在這根據需求提示第幾張圖片上傳失敗 resolve({ error: err, index }) }, this); }); }