最近在作一個複雜的跨五端(PC、H五、小程序、iOS、android)的需求,歷時將近兩個多月,其中酸甜苦辣冷暖自知,近日終於能夠封板上線,回憶整個開發過程,深坑不斷,收穫頗多,今天先分享一下在微信小程序的web-view裏選取照片的功能,此文完整的記錄整個開發過程,完全解決各類疑難雜症。javascript
由於跨端因此最開始使用了<input type="file" accept="image/*" capture="camera">
的方式,簡單粗暴可以使用,五端勉強均可以打個及格分,一直到聯調結束PM檢測時說體驗太差了,趁着還有時間(其實我也看不下去),索性就按PM的要求來,開始計劃調用原生的,由於客戶端以前已經提供了這樣的bridge,因此和客戶端的頭像調試輕鬆完成,剩下的小程序歷經萬千磨難,最終完美謝幕。php
首先沒有作過這方面的經驗,兩眼一抹黑,因爲是把H5頁面嵌套在小程序的web-view裏,因此直接查看官方文檔,小程序向web-view提供了三十多個API-詳情文檔。至此覺得能夠輕鬆搞定,因而開始了不斷的趟坑。。。html
剛開始直接在H5裏使用了wx.chooseImage
,發如今開發者工具中不斷的報錯the permission value is offline verifying
,慢慢開始搜索才發如今小程序的web-view裏也必須使用jweixin,其實就是個公衆號網頁,接下來開始按這套流程走:java
// 推薦使用1.3.2以上的版本,以前的版本不少坑😭
https://res.wx.qq.com/open/js/jweixin-1.3.2.js
// 我是直接用的npm包,目前基於1.4.0-test的版本
npm install weixin-js-sdk
複製代碼
wx.config({
debug: true, // 開啓調試模式,調用的全部api的返回值會在客戶端alert出來,若要查看傳入的參數,能夠在pc端打開,參數信息會經過log打出,僅在pc端時纔會打印。
appId: '', // 必填,微信公衆號的惟一標識,此處填寫公衆號的appId
timestamp: , // 必填,生成簽名的時間戳
nonceStr: '', // 必填,生成簽名的隨機串
signature: '',// 必填,簽名
jsApiList: [] // 必填,須要使用的JS接口列表,全部JS接口列表見附錄2
});
複製代碼
wx.ready(function(){
// config信息驗證後會執行ready方法,全部接口調用都必須在config接口得到結果以後,config是一個客戶端的異步操做,因此若是須要在頁面加載時就調用相關接口,則須把相關接口放在ready函數中調用來確保正確執行。對於用戶觸發時才調用的接口,則能夠直接調用,不須要放在ready函數中。
});
複製代碼
wx.error(function(res){
// config信息驗證失敗會執行error函數,如簽名過時致使驗證失敗,具體錯誤信息能夠打開config的debug模式查看,也能夠在返回的res參數中查看,對於SPA能夠在這裏更新簽名。
});
複製代碼
wx.checkJsApi({
jsApiList: ['chooseImage'] // 須要檢測的JS接口列表
success: function(res) {
// 以鍵值對的形式返回,可用的api值true,不可用爲false
// 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
},
fail: function(err) {
// checkJsApi接口調用失敗
}
});
複製代碼
wx.chooseImage({
count: 1, // 默認9
sizeType: ['original', 'compressed'], // 能夠指定是原圖仍是壓縮圖,默認兩者都有
sourceType: ['album', 'camera'], // 能夠指定來源是相冊仍是相機,默認兩者都有
success: function (res) {
var localIds = res.localIds; // 返回選定照片的本地ID列表,localId能夠做爲img標籤的src屬性顯示圖片
}
});
複製代碼
本部分要特別注意如下幾點:android
wx.chooseImage
獲取到的圖片爲一個臨時路徑,微信同時提供了wx.getLocalImgData
方法能夠把獲取到的路徑轉爲base64格式的數據,至此就能夠輕鬆許多了,可是轉出來的base64在android和iOS中稍有不一樣,須要特別注意一下:web
wx.getLocalImgData({
localId: req.localIds[0].toString(),
success: function (res) {
const localData = res.localData;
let imageBase64 = '';
if (localData.indexOf('data:image') == 0) {
//蘋果的直接賦值,默認生成'data:image/jpeg;base64,'的頭部拼接
imageBase64 = localData;
} else {
//此處是安卓中的惟一得坑!在拼接前須要對localData進行換行符的全局替換
//此時一個正常的base64圖片路徑就完美生成賦值到img的src中了
imageBase64 = 'data:image/jpeg;base64,' + localData.replace(/\n/g, '');
}
}
});
複製代碼
獲取到圖片的base64數據以後其實咱們就能夠隨心所欲了,不須要使用
wx.uploadImage
和wx.downloadImage
了,不只須要麻煩的上傳到微信服務器,還有三天的時間限制反而增長了成本,最好的方式就是直接上傳至咱們本身的服務器。npm
通常與服務端交互的這種文件類型通常採用表單提交--multipart/form-data的方式,這就要求咱們熟悉傳輸form-data的數據交互,因此須要把剛纔獲取到的base64轉爲可post的二進制數據,JavaScript提供了原生的atob/btoa
用來對base64進行編碼和解碼;json
base64ToBlob(dataurl) {
let arr = dataurl.split(',');
let mime = arr[0].match(/:(.*?);/)[1];
let bstr = atob(arr[1]);
let n = bstr.length;
let u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
}
複製代碼
ok,處理好可交互的數據就能夠上傳到本身的服務器了,以後至關於普通H5頁面的圖片交互了,可上傳可下載😄小程序
let param = new FormData();
param.append('headPic', imageData);
this.$http.post('/upload.json', {
headers: {
'Content-Type': 'multipart/form-data',
},
params: param,
}).then(res => {
console.log('上傳成功', res);
});
複製代碼
wx.chooseImage
成功調用後iOS和android分別調起系統原生的拍照和相冊,分別以下圖:微信小程序
終於。。。能夠告一段落了,至此小程序的web-view
裏成功使用了wx.chooseImage
,下面把完整代碼貼出來,敬請參考😄
<template>
<div class="card-avatar">
<img
:src="avatarUrl"
>
<div
class="choose-image"
@click="chooseImage"
/>
</div>
</template>
<script>
import wx from 'weixin-js-sdk';
const imgUrl = require('../../common/assets/images/default-avatar.png');
export default {
name: 'Card',
components: {
},
props: {
},
data() {
return {
avatarUrl: imgUrl,
};
},
computed: {
},
watch: {
},
mounted() {
this.getWxConfigDatas();
},
methods: {
getWxConfigDatas() {
const params = {
checkCode: getUrlParams().checkCode, // 從地址欄獲取checkCode用於校驗signature
};
this.$http.get('/signature.json', { params }).then(res => {
if (res.content.data && res.content.data.signature) {
this.registereWxApi(res.content.data.signature);
}
});
},
registereWxApi(data) {
wx.config({
// debug: true,
jsApiList: ['chooseImage', 'getLocalImgData'],
appId: data.appId,
timestamp: data.timestamp,
nonceStr: data.nonceStr,
signature: data.signature,
});
},
chooseImage() {
let that = this;
if (window.__wxjs_environment === 'miniprogram') {
wx.miniProgram.getEnv(function(res) {
if (res.miniprogram) {
wx.checkJsApi({
jsApiList: ['chooseImage', 'getLocalImgData'],
success: function(res) {
if (res.checkResult.chooseImage) {
wx.chooseImage({
count: 1,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: function(req) {
wx.getLocalImgData({
localId: req.localIds[0].toString(),
success: function (res) {
const localData = res.localData;
let imageBase64 = '';
if (localData.indexOf('data:image') == 0) {
//蘋果的直接賦值,默認生成'data:image/jpeg;base64,'的頭部拼接
imageBase64 = localData;
} else {
//此處是安卓中的惟一得坑!在拼接前須要對localData進行換行符的全局替換
//此時一個正常的base64圖片路徑就完美生成賦值到img的src中了
imageBase64 = 'data:image/jpeg;base64,' + localData.replace(/\n/g, '');
}
that.avatarUrl = imageBase64;
that.handleAvatar(that.dataURLtoBlob(imageBase64));
}
});
},
fail() {
that.$toast.show({
type: 'text',
text: '選擇頭像失敗!',
});
}
});
} else {
that.$toast.show({
type: 'text',
text: '暫不支持修改頭像!',
});
}
},
fail: function() {
that.$toast.show({
type: 'text',
text: '暫不支持修改頭像!',
});
},
});
} else {
that.lgBridgeChooseImage();
}
});
} else {
that.lgBridgeChooseImage();
}
},
handleAvatar(imageData) {
let that = this;
let param = new FormData();
param.append('headPic', imageData);
that.$http.post('/upload.json', {
headers: {
'Content-Type': 'multipart/form-data',
},
params: param,
}).then(res => {
if (parseInt(res.state, 10) === 1) {
that.$toast.show({
type: 'success',
text: '上傳成功!',
});
that.avatarUrl = res.content && res.content.data && res.content.data.url;
}
});
},
dataURLtoBlob(dataurl) {
let arr = dataurl.split(',');
let mime = arr[0].match(/:(.*?);/)[1];
let bstr = atob(arr[1]);
let n = bstr.length;
let u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new Blob([u8arr], { type: mime });
}
}
};
</script>
<style lang="less" scoped>
.card-avatar {
position: absolute;
top: 0;
left: 50%;
transform: translate(-50%, -50%);
width: 180px;
height: 180px;
border-radius: 100%;
overflow: hidden;
img {
width: 100%;
height: 100%;
}
.choose-image, input {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
opacity: 0;
}
}
</style>
複製代碼
這個需求作下來參考了很多文章,其實大多都是隻知其一;不知其二,每一個人遇到的問題不一樣,這篇文章能夠說完全解決wx.chooseImage
的各類疑難雜症,一步到位,所以趁着今天休息把整個過程記錄下來,分享給以後有須要的各位同行!
精彩預告:最近歷時兩個月的大需求遇到不少深坑,接下來會相繼分享出來,以此共勉😂
更多精彩內容歡迎關注個人公衆號【天道酬勤Lewis】