最近在作一个复杂的跨五端(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】