UniApp 《多图上传性能优化》

278 阅读2分钟

在uniapp中,通常使用两个api实现本地图片选择和图片上传

问题引入:

1、uploadFile是官方封装的,提供了一个回调函数参数 success来处理上传成功的情况。

2、上传是异步的,当有多图进行上传时,官方建议使用循环遍历该api的方式来进行上传,但是由于封装过了,无法async await的方式来控制上传结果,特别是统一把握多图的上传情况,进行后续业务逻辑。

怎么解决:

1、本文提供了一种思路,使用 promise 再次封装 uni.uploadFile,最后使用 promise.all来实现并发执行上传请求,并保持代码逻辑以同步方式顺序执行

2、使用filter过滤请求结果,更加优雅

一、封装 uni.uploadFilepromise

// ============= 防止异步,特地将《图片上传》封装成async await,在成功后调用 =================、
// 思考:不要做过多的提示,既然是封装,就应该让使用者知道,自己处理错误信息
const single_upload_promise = async (_filePath) => {
  return new Promise((resolve, reject) => {
    // --------- 上传图片到云端 ----------
    uni.uploadFile({
      url: import.meta.env.VITE_UPLOAD_BASEURL, // 从Vite的env中导入全局配置的上传地址,代码更优雅
      filePath: _filePath,
      name: "file",
      // timeout: 30000, // 超时时间 30s
      success: (uploadFileRes) => {
        const res_upload = JSON.parse(uploadFileRes.data)
        // -------这里举例 上传成功后,后端返回的json数据 ---------
        // { 
        // "code":200,
        // "msg":"上传成功",
        // "time":"1745388478",
        // "data":{
        //    "url":"\/1141\/uploads\/20250423\/3a452344109269b05a3e8800aefaa7be.jpg",
        //    "fullurl":"https:\/\/mkxy.oss-cn-shenzhen.aliyuncs.com\/1141\/uploads\/20250423\/3a452344109269b05a3e8800aefaa7be.jpg"
        // }}
        
        
        // ---- 接口报错 ----
        if (res_upload.code != 200) {
          reject(res_upload.msg) // 失败后,将错误信息返回给promis
          return
        }
        // ---- 接口成功 ----
        resolve(res_upload.data.fullurl) // 成功后,将图片url返回给promis
      },
      // ---- 请求失败 ----
      fail: (err) => {
        reject(err) // 失败后,将错误信息返回给promis
      },
    })
  })
}

二、以promise.all实现并行上传,并控制代码为同步执行

// ============= 本地图片上传 =================
const handleUpload_LocalDCMI = () => {
  // 打开相册选择图片
  uni.chooseImage({
    count: 9,
    sizeType: ["original", "compressed"],
    sourceType: ["album", "camera"],
    success: async ({ tempFiles, tempFilePaths }) => {
      // --------- 图片校验 ----------
      let img_checked_flag = true // 图片校验通过标志 默认通过
      const ALLOWED_EXTENSIONS = ["jpg", "jpeg", "png"]
      for (let _file of tempFiles) {
        const extension_end = _file.name.split(".").pop().toLowerCase()

        if (!ALLOWED_EXTENSIONS.includes(extension_end)) {
          toast.error(`不支持的文件类型:${_file.name}, 请选择JPG/PNG/JPEG格式`)
          img_checked_flag = false // 图片校验不通过
          return // 立即终止当前 handleUpload_LocalDCMI 函数的执行
        }
      }

      // --------- 并行形式上传图片到oss ----------
      // 实现并行上传所有图片,并await使得代码以同步的方式执行
      let oss_urls = (
        await Promise.all(
          // 超屌处理
          tempFilePaths.map((item) =>
            single_upload_promise(item)
              .then((res) => {
                // 上传成功后,将图片url返回给promis
                return res
              })
              .catch((err) => {
                toast.error(`上传${item}失败:${err}`)
                return false // 上传失败,返回 false,到时候过滤掉
              }),
          ),
        )
      ).filter(Boolean)


      // --------- 上述代码是同步的,到这里已经全上传完了---------
      console.log("用户所选的所有图,已上传到oss成功 oss_urls: ", oss_urls)
      
      // 结果数据会变成这样: oss_urls = ["https://aaa.png","https://aaa.png","https://aaa.png"]
      // ------------------- 后续业务逻辑处理 -------------------
     if (
        oss_urls.length > 0 ||
        oss_urls.length == tempFilePaths.length
      ) {
        // 上传成功
        toast.success("全部上传成功")
      } else {
        // 上传失败
        toast.error("部分上传成功")
      }
      // ......省略业务逻辑代码

      
    },
  })
}