资讯专栏INFORMATION COLUMN

react利用await/async实现批量上传图片可预览可限制上传图片数量

lindroid / 1699人阅读

摘要:本文在这里要讲的是批量上传多张图片时,如果不分批上传可能触发浏览器的并发限制,亦或是图片过多过大导致上传超时,都会影响图片的上传成功率。所以,我们需要分批上传图片时,概念就能很好的解决我们的问题。

es7提出的async/await概念已经存在有相当长一段时间,具体概念用法就不在这里赘述了,优势在于处理解决then链多层嵌套回调的问题,使得代码更为简单清晰。

本文在这里要讲的是批量上传多张图片时,如果不分批上传可能触发浏览器的并发限制,亦或是图片过多过大导致上传超时,都会影响图片的上传成功率。所以,我们需要分批上传图片时,async/await概念就能很好的解决我们的问题。否则,就只能使用递归来处理,或是只允许单张上传影响用户体验。

其实全部代码和react没有太大关系,只是用到部分特性,是可以适用于任何框架的。至于批量获取图片的组件我直接用的是react-dropzone,可以拖拽图片,当然了,使用原生的
也是完全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(", ")}等文件已存在;
`; } if (rejectedFiles.length) { const rejectedFilesName = rejectedFiles.map(item => `"${item.name}"`); toastMessage = `${toastMessage}${rejectedFilesName.join(", ")}等文件不符合上传条件;
`; } 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(", ")}等文件超出上传数量的限制;
`; } 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 }); } }

批量上传代码

// 批量上传获取的图片
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);
    });
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/107902.html

相关文章

  • 小程序异步问题:多个网络请求依次执行并依次收集请求结果

    摘要:异步带来的问题小程序的网络请求是异步的我们无法通过来将网络请求结果返回出来使用。省略其他属性接业务逻辑代码例如这个微信的网络请求,我们可以通过和的回调函数来读取的值从而完成依赖结果的业务逻辑。 业务逻辑 最近开发一个便签小程序的时候,有这样一个需求:用户可以在写便签的时候添加一个或多个图片。 对于这个需求,我们用户按下保存键时,内部具体的实现上是这样的逻辑: 首先检测用户是否传入了图...

    Elle 评论0 收藏0
  • 一些基于React、Vue、Node.js、MongoDB技术栈的实践项目

    摘要:利用中间件实现异步请求,实现两个用户角色实时通信。目前还未深入了解的一些概念。往后会写更多的前后台联通的项目。删除分组会连同组内的所有图片一起删除。算是对自己上次用写后台的一个强化,项目文章在这里。后来一直没动,前些日子才把后续的完善。 欢迎访问我的个人网站:http://www.neroht.com/ 刚学vue和react时,利用业余时间写的关于这两个框架的训练,都相对简单,有的...

    tangr206 评论0 收藏0
  • 分享一个react 图片上传组件 支持OSS 七牛云

    摘要:是一个基于组件的图片上传组件支持等服务端自定义获取签名批量上传预览删除排序等功能需要版本大于支持地址安装引入调用图片上传服务地址图片查看地址前缀配置项类型默认值描述类型目前 react-uplod-img 是一个基于 React antd组件的图片上传组件 支持oss qiniu等服务端自定义获取签名,批量上传, 预览, 删除, 排序等功能 需要 react 版本大于 v16.1.0 ...

    gself 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<