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); }); }