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


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

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)行記得在原生編輯器右上角的詳情里將下面的打勾喲~
