微信小程序多图上传+异步操作的同步执行+wx.upload的封装

最近刚接触小程序,需要做多图上传,于是找了很多资料,遇到很多困难,所以记录下以便反复琢磨。
界面写的比较简单,仅供学习,大致效果和功能如下:


image.png
image.png

整体项目文件结构如下


image.png

upload.wxml如下,有添加图片,图片预览,删除图片,上传图片功能


  上传图片
  
    
      
        
        
        
        
      
      
      
        
      
    
  
  

upload.wxss如下

.multipleImg {
  width: 100%;
  display: flex;
  flex-flow: row wrap;
}
.upload {
  position: relative;  
}
.addImg {
  width: 200rpx;
  height: 200rpx;
  margin-left: 40rpx;
}
.deleteImg {
  width: 30rpx;
  height: 30rpx;
  position: absolute;
  bottom: 20rpx;
  right: 10rpx;
  opacity: 1.5;
}
.text{
  margin-left: 40rpx;
}
.img-box{
  border-bottom:solid 2rpx #eee;
}

然后是最重要的JS部分,首先放上不考虑异步同步问题,直接一顺写下来的版本
支持最多9张图片上传,执行wx.chooseImage后,回调success,拿到本地图片URL,开始判断选择的图片是否符合要求,图片大小,图片格式都有判断,其中格式判断挺有意思,拿到的图片信息是个xxx.xxx.xxx.jpg的形式,需要切分一下拿末尾的格式和允许的格式对比,满足才行。
然后是网络上传部分,一开始没有考虑异步同步问题,想着把拿来的本地图片URL一起上传完事儿了,于是就有了第一个版本,这个版本里最后只拿了返回图片的URL后缀部分放到本地data的upload数组里,下面第二版本完善了URL拼接的代码:

data: {
    upload: [], // 上传的图的URL
    chooseImgs: [], // 用户选择上传的图片存到UI界面上的数组
  },
  uploadImgs:function() {
    const that = this;
    const {
      chooseImgs
    } = this.data;
    wx.chooseImage({
      count: 9 - chooseImgs.length, // 默认9
      sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
      sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
      success: function (res) {
        console.log(res)
        const newChooseImgs = res.tempFilePaths; //拿到图片本地URL(URL,长度)
        const imgInfo = res.tempFiles; //拿到图片的信息(URL,大小,长度)
        // 判断选择的图片是否符合要求
        for (let i = 0; i < imgInfo.length; i++) {
          console.log("尺寸", imgInfo[i].size);
          // 判断图片尺寸,大于10M不行
          if (imgInfo[i].size / 1024 / 1024 >= 10) {
            wx.showModal({
              title: '提示', // 标题
              content: "图片超过10MB啦~", // 内容
              showCancel: false, // 是否显示取消按钮
              confirmText: '确定', // 确认按钮的文字
            });
            return
          }
          // 判断图片格式
          const imgSplit = imgInfo[i].path.split(".");
          const imgSLen = imgSplit.length;
          console.log("格式", imgSplit, imgSLen, imgSLen - 1);
          //分割图片URL后有四段,但是下标是0,1,2,3,所以需要总数减1才是后缀的下标
          if (["jpg", "jpeg", "png"].includes(imgSplit[imgSLen - 1])) {
            console.log("格式正确");
          } else {
            console.log("格式错误");
            utils.showModalInfo({
              content: "请选择正确的图片格式上传",
            });
            return
          }
        }
        console.log("选择图片之前", res, chooseImgs, newChooseImgs);
        //此时chooseImgs的池子里是空,需要把选择到newChooseImgs里的图片传入本地chooseImgs池子里
        newChooseImgs.forEach(item => {
          chooseImgs.push(item);
        });
        console.log("选择图片后", chooseImgs, newChooseImgs); //此时池子里有图片
        // 限制上传数量
        if (chooseImgs.length > 9) {
          wx.showModal({
            title: '提示',
            content: "请选择正确的图片格式上传",
            showCancel: false,
            confirmText: '确定',
          });
        }
        // 判断是否显示添加图片
        console.log("显示添加图片", chooseImgs.length);
        if (chooseImgs.length > 0) {
          //图如果满了9张,不显示加图
          if (chooseImgs.length >= 9) {
            that.setData({
              //隐藏加号
              hideAdd: true
            })
          } else {
            that.setData({
              hideAdd: false
            })
          }
          // 显示预览图
          that.setData({
            chooseImgs
          });

          //  网络请求 上传图片
          const requestMsg = [];
          newChooseImgs.forEach(item => {
            wx.uploadFile({
              url: 'xxxxx', // xxxxx为上传图片的接口
              filePath: item,
              header: {
                'content-type': 'multipart/form-data'
              },
              name: 'file',
              formData: {
                'dir': "goods",
                'tokenId': 'xxxxxxxxxxx'
              },
              success: function (e) {
                console.log("访问上传接口成功", e);
                const data = JSON.parse(e.data);
                const imgHouZhui = data.name
                //把每次选择的图push进数组
                const upload = that.data.upload;
                console.log("上传之前的图数组", upload, imgHouZhui);
                upload.push({
                  imgHouZhui: imgHouZhui,
                  isShow: false,
                  requestMsg, // 上传返回信息代号
                });
                that.setData({
                  upload,
                });
                console.log("上传之后的图数组", upload, data);
              },
              fail: function (e) {
                console.log("访问接口失败", e);
                wx.showToast({
                  title: "网络出错,上传失败",
                  icon: 'none',
                  duration: 1000
                });
              },
            });
          });
        }
      }
    })
  },
/***预览图片***/
previewImg: function (e) {
    console.log(e)
    const contentImg = e.currentTarget.dataset.item; //item就是图片url
    //console.log("点击图片放大预览", contentImg);
    wx.previewImage({
      current: contentImg, //当前图片地址
      urls: [contentImg], //所有要预览的图片的地址集合 数组形式
      success: function (res) {},
      fail: function (res) {},
      complete: function (res) {},
    })
  },
/***删除图片***/
  deleteImg: function (e) {
    const index = e.currentTarget.dataset.index; //index是图片的索引,从0开始
    const {
      upload,
      chooseImgs
    } = this.data;
    console.log("删除前的图片", upload)
    upload.splice(index, 1);
    chooseImgs.splice(index, 1);
    //console.log("点击图片删除", index);
    this.setData({
      upload,
      chooseImgs,
      hideAdd: chooseImgs.length === 9, // 是否隐藏添加图片的图标
    })
    console.log("删除后的图片", upload)
  },

后来老师小哥说怎么保证我是一张图片接着一张图片传,也就是一张传成功才传下一张,因为上面使用的forEach是异步操作,因此建议我改写成异步操作同步执行的形式。然后就开始各种查资料,过程中发现了一些值得记下来的句子,promise,async,await就是解决这个问题的

1.promise是解决forEach循环调用的异步方法的同步实现过程
2.为什么要用await?使我们的异步代码更像同步
3.await如果它等到的不是一个promise对象,那await表达式的运算结果就是它等的东西,
如果它等到的是一个promise对象,比如async返回的就是一个promise对象,那么它就会阻塞后面的代码(只阻塞当前路径,不阻塞别的路径),等待promise对象进入resolve状态,然后得到resolve的值,作为await表达式的运算结果。

那么问题来了,根据async,await的写法,我得把上传函数wx.upload封装起来模块化,这样比较好写一点,于是开始封装
首先在utils文件里新建config.js保存下接口

const UPLOAD_URL = 'xxxxxxxxxx' // 此处为服务器地址
module.exports = {
    UPLOAD_URL
};

然后找到utils.js导入

import {
  UPLOAD_URL
} from './config'

然后编写封装代码,最后exports

const uploadFile = (uploadFile) => {
  return new Promise((resolve, reject) => {
    wx.uploadFile({
      url: UPLOAD_URL, // 上传的服务器接口地址
      filePath: uploadFile,
      header: {
        "Content-Type": "multipart/form-data",        
      },
      name: 'file', //上传的所需字段,后端提供
      formData: {
        'dir': "goods",
        'tokenId': 'xxxxxxxxxx'
      },
      success: (res) => {
        // 上传完成操作
        const data = JSON.parse(res.data)
        resolve({
          data: data
        })
      },
      fail: (err) => {
        //上传失败:修改pedding为reject
        console.log("访问接口失败", err);
        wx.showToast({
          title: "网络出错,上传失败",
          icon: 'none',
          duration: 1000
        });
        reject(err)
      }
    });
  })
}

module.exports = {
  formatTime: formatTime,
  uploadFile: uploadFile
}

然后应用到页面上,在upload.js上面import

import {
  uploadFile
} from '../../utils/util'

然后修改上面的上传图片方法,首先把

 uploadImgs:function() {}   改成    async uploadImgs() {}
success: function (res) {}   改成   success: async function (res) {} 

最后只需要修改//网络请求 上传图片 部分的代码如下,resName就是上传后接口返回的图片URL后缀,因此和预先给的接口前缀拼起来就是这张图片完整的URL,然后存到本地的data里的upload数组里供后续任务使用,完整URL可以放到浏览器里看看能不能显示,能显示就代表上传成功。

   for (let item of newChooseImgs) {
            console.log(item)
            let resImage = await uploadFile(newChooseImgs[0])
            console.log("await返回的内容resImage:",resImage)
            const upload = that.data.upload;//把每次选择的图push进数组
            const resStatus = resImage.data.status
            const resName = resImage.data.name
            console.log("11111",resName)
            upload.push({
              imgURL:'xxxxxxxx'+  resName,
            });
            if (resStatus == 1) {
              that.setData({
                "upload": upload
              })
              console.log("本地存储的图片URL后缀:",that.data.upload)
              // 
            }

          }

至此完成了多图上传的功能~
后续会整理更新图片的代码
对了,本地运行记得在原生编辑器右上角的详情里将下面的打勾哟~


image.png

你可能感兴趣的:(微信小程序多图上传+异步操作的同步执行+wx.upload的封装)