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

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


image.png
image.png

整体项目文件结构如下


image.png

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

<view class="page-box">
  <view class="text">上传图片</view>
  <view class="img-box">
    <view class="multipleImg">
      <view class='upload' wx:for="{{chooseImgs}}" wx:key>
        <!--显示缩略图-->
        <image src="{{item}}" class="addImg" mode="aspectFill" bindtap="previewImg" data-index='{{index}}'
          data-item='{{item}}' />
        <!--删除-->
        <image src="../../img/deleteImg.png" class="deleteImg" mode="aspectFill" bindtap="deleteImg"
          data-index='{{index}}' data-item='{{item}}' />
      </view>
      <!--上传图片-->
      <view class='upload' style='display:{{hideAdd?"none":"block"}}'>
        <image src='../../img/add.png' class="addImg" bindtap="uploadImgs" mode="aspectFit" />
      </view>
    </view>
  </view>
  <!--上传图片结束-->

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
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容