微信小程序多圖上傳+異步操作的同步執(zhí)行+wx.upload的封裝

最近剛接觸小程序,需要做多圖上傳,于是找了很多資料,遇到很多困難,所以記錄下以便反復(fù)琢磨。
界面寫的比較簡單,僅供學(xué)習(xí),大致效果和功能如下:


image.png
image.png

整體項(xiàng)目文件結(jié)構(gòu)如下


image.png

upload.wxml如下,有添加圖片,圖片預(yù)覽,刪除圖片,上傳圖片功能

<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>
  <!--上傳圖片結(jié)束-->

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張圖片上傳,執(zhí)行wx.chooseImage后,回調(diào)success,拿到本地圖片URL,開始判斷選擇的圖片是否符合要求,圖片大小,圖片格式都有判斷,其中格式判斷挺有意思,拿到的圖片信息是個xxx.xxx.xxx.jpg的形式,需要切分一下拿末尾的格式和允許的格式對比,滿足才行。
然后是網(wǎng)絡(luò)上傳部分,一開始沒有考慮異步同步問題,想著把拿來的本地圖片URL一起上傳完事兒了,于是就有了第一個版本,這個版本里最后只拿了返回圖片的URL后綴部分放到本地data的upload數(shù)組里,下面第二版本完善了URL拼接的代碼:

data: {
    upload: [], // 上傳的圖的URL
    chooseImgs: [], // 用戶選擇上傳的圖片存到UI界面上的數(shù)組
  },
  uploadImgs:function() {
    const that = this;
    const {
      chooseImgs
    } = this.data;
    wx.chooseImage({
      count: 9 - chooseImgs.length, // 默認(rèn)9
      sizeType: ['original', 'compressed'], // 可以指定是原圖還是壓縮圖,默認(rèn)二者都有
      sourceType: ['album', 'camera'], // 可以指定來源是相冊還是相機(jī),默認(rèn)二者都有
      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: '提示', // 標(biāo)題
              content: "圖片超過10MB啦~", // 內(nèi)容
              showCancel: false, // 是否顯示取消按鈕
              confirmText: '確定', // 確認(rèn)按鈕的文字
            });
            return
          }
          // 判斷圖片格式
          const imgSplit = imgInfo[i].path.split(".");
          const imgSLen = imgSplit.length;
          console.log("格式", imgSplit, imgSLen, imgSLen - 1);
          //分割圖片URL后有四段,但是下標(biāo)是0,1,2,3,所以需要總數(shù)減1才是后綴的下標(biāo)
          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); //此時池子里有圖片
        // 限制上傳數(shù)量
        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
            })
          }
          // 顯示預(yù)覽圖
          that.setData({
            chooseImgs
          });

          //  網(wǎng)絡(luò)請求 上傳圖片
          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進(jìn)數(shù)組
                const upload = that.data.upload;
                console.log("上傳之前的圖數(shù)組", upload, imgHouZhui);
                upload.push({
                  imgHouZhui: imgHouZhui,
                  isShow: false,
                  requestMsg, // 上傳返回信息代號
                });
                that.setData({
                  upload,
                });
                console.log("上傳之后的圖數(shù)組", upload, data);
              },
              fail: function (e) {
                console.log("訪問接口失敗", e);
                wx.showToast({
                  title: "網(wǎng)絡(luò)出錯,上傳失敗",
                  icon: 'none',
                  duration: 1000
                });
              },
            });
          });
        }
      }
    })
  },
/***預(yù)覽圖片***/
previewImg: function (e) {
    console.log(e)
    const contentImg = e.currentTarget.dataset.item; //item就是圖片url
    //console.log("點(diǎn)擊圖片放大預(yù)覽", contentImg);
    wx.previewImage({
      current: contentImg, //當(dāng)前圖片地址
      urls: [contentImg], //所有要預(yù)覽的圖片的地址集合 數(shù)組形式
      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("點(diǎn)擊圖片刪除", index);
    this.setData({
      upload,
      chooseImgs,
      hideAdd: chooseImgs.length === 9, // 是否隱藏添加圖片的圖標(biāo)
    })
    console.log("刪除后的圖片", upload)
  },

后來老師小哥說怎么保證我是一張圖片接著一張圖片傳,也就是一張傳成功才傳下一張,因?yàn)樯厦媸褂玫膄orEach是異步操作,因此建議我改寫成異步操作同步執(zhí)行的形式。然后就開始各種查資料,過程中發(fā)現(xiàn)了一些值得記下來的句子,promise,async,await就是解決這個問題的

1.promise是解決forEach循環(huán)調(diào)用的異步方法的同步實(shí)現(xiàn)過程
2.為什么要用await?使我們的異步代碼更像同步
3.await如果它等到的不是一個promise對象,那await表達(dá)式的運(yùn)算結(jié)果就是它等的東西,
如果它等到的是一個promise對象,比如async返回的就是一個promise對象,那么它就會阻塞后面的代碼(只阻塞當(dāng)前路徑,不阻塞別的路徑),等待promise對象進(jìn)入resolve狀態(tài),然后得到resolve的值,作為await表達(dá)式的運(yùn)算結(jié)果。

那么問題來了,根據(jù)async,await的寫法,我得把上傳函數(shù)wx.upload封裝起來模塊化,這樣比較好寫一點(diǎn),于是開始封裝
首先在utils文件里新建config.js保存下接口

const UPLOAD_URL = 'xxxxxxxxxx' // 此處為服務(wù)器地址
module.exports = {
    UPLOAD_URL
};

然后找到utils.js導(dǎo)入

import {
  UPLOAD_URL
} from './config'

然后編寫封裝代碼,最后exports

const uploadFile = (uploadFile) => {
  return new Promise((resolve, reject) => {
    wx.uploadFile({
      url: UPLOAD_URL, // 上傳的服務(wù)器接口地址
      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) => {
        //上傳失?。盒薷膒edding為reject
        console.log("訪問接口失敗", err);
        wx.showToast({
          title: "網(wǎng)絡(luò)出錯,上傳失敗",
          icon: 'none',
          duration: 1000
        });
        reject(err)
      }
    });
  })
}

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

然后應(yīng)用到頁面上,在upload.js上面import

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

然后修改上面的上傳圖片方法,首先把

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

最后只需要修改//網(wǎng)絡(luò)請求 上傳圖片 部分的代碼如下,resName就是上傳后接口返回的圖片URL后綴,因此和預(yù)先給的接口前綴拼起來就是這張圖片完整的URL,然后存到本地的data里的upload數(shù)組里供后續(xù)任務(wù)使用,完整URL可以放到瀏覽器里看看能不能顯示,能顯示就代表上傳成功。

   for (let item of newChooseImgs) {
            console.log(item)
            let resImage = await uploadFile(newChooseImgs[0])
            console.log("await返回的內(nèi)容resImage:",resImage)
            const upload = that.data.upload;//把每次選擇的圖push進(jìn)數(shù)組
            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)
              // 
            }

          }

至此完成了多圖上傳的功能~
后續(xù)會整理更新圖片的代碼
對了,本地運(yùn)行記得在原生編輯器右上角的詳情里將下面的打勾喲~


image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容