最近做项目,其中有个需求是在微信企业号上实现图片上传操作。一开始我就把重点放在了"移动端图片上传"而不是"微信图片上传",所以各种查资料,找插件,费了好些功夫,实现起来也是各种困难。后来接触到微信公众号,稍微了解了一下,才知道利用微信公众号或者现在已经升级为企业微信的企业号的API,还是挺简单的一个功能。下面我就详细整理一下整个过程,做个笔记,一方面可以帮助到从未接触过这个功能的同学,一方面也方便自己日后翻阅。
1、首先进入微信公众平台,查看JS-SDK开发文档。https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115
2、开发文档有详细的步骤说明,但我还是根据我自己做的过程再稍微记录一下。
第一步中的绑定安全域名可不做,不会影响功能的实现。我直接从第二部引进js文件开始的。当我们想要通过微信上传图片时,有两种方法:一种是JS文件调用,一种是接口调用。这里用的是第一种,通过引进JS文件,然后直接调用接口,不再使用URL等接口地址。
3、JS文件的引入:
4、通过config接口注入权限验证配置:
这一步尤为重要。
1)在开发调试时,可将debug设为true,这样可以看到返回的信息,方便调试bug。但在生产环境上,记得设为false。
2)appId是公众号的唯一标识。项目挂靠在哪个公众号或者企业号,就去找相关负责人拿到appId,这个是固定的值,没有改动,也不需要调用获取。(服务器端,也就是后端开发人员去拿到这个值)
3)除了jsApiList的值需要前端人员根据需要自己写以外,其他参数均需从后端获取。最重要的是其中的签名串,有其特定的生成规则,后端需要根据一定的规则,去调用微信的接口,获取相关参数后生成签名串,然后再将对应的参数传给前端。前端人员获取到这些数据信息后,就可以完成配置信息的注入了。
4)可能会遇到的问题,当在完成配置信息的注入后,实际开始调用所需接口时,出现报错信息:invalid signature,permission denied,此时解决方法如下:
①首选检查生成签名串的算法(规则)是否正确;
② 其次需要特备注意的是:你在利用参数生成签名的时候,要对所有待签名参数按照字段名的 ASCII 码从小到大排序(字典序)后,使用 URL 键值对的格式(即key1=value1&key2=value2…)拼接成字符串 string1。这里需要注意的是所有参数名均为小写字符。
③注意参数key值大小写问题;
④生成签名串时,需要前端人员给后端传当前页面的URL,但不包括'#'hash后面的部分。注意前端传值时的格式以及后端接收时的解析方式。
⑤确认 config 中的 appid 与用来获取 jsapi_ticket 的 appid 一致。
⑥很重要的一点,确认当前访问页面的URL已经被项目挂靠的公众号或者企业号授权访问。
更多错误信息及详细解决方法,请参考:https://my.oschina.net/u/2308739/blog/371414
5)jsApiList中需要填写的接口。(其实按现在这个操作,是不需要下载图片接口的,因为下载这一操作是由后端实现的。)
5、配置信息注入并且成功校验完之后,就可以在wx.ready接口下操作相关图片接口了。
6、在wx.ready下调用图片选择、上传、预览等接口时,直接按照操作的顺序调用即可。但其中涉及到的一个问题是,如果前端没有自己的后台,需要和另外一台服务器上的后台进行交互,此时在图片上传时就需要调用后端接口,将图片上传后生成的文件流,最终得到的mediaId传给后端,然后让后端去微信服务器上下载刚刚上传的图片。
7、话不多说,直接上全部代码。
/** ******************获取微信配置数据********************** */
var Url = ""; //服务器接口路径
var oldNet = '';
var newNet = '';//记录网络状态
var configData;
$.ajax({
url: Url + '/GetSignatureInfo',//后端接口名,可自定义。
// data: {pageUrl: location.href.split('#')[0]},
type:"get",
//params意为参数,是自定义的,用以表明这是传给后台的数据。
data:{"params":location.href.split('#')[0]},
contentType:"application/json; charset=utf-8",
//数据类型为jsonp,解决跨域问题。
dataType:"jsonp",
//自定义的jsonp回调函数名,默认为jQuery自动生成的随机函数
jsonpCallback:"success_jsonpCallback_select",
//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(默认为callback)
jsonp:"callbackparam",
success: function (data) {
// alert(JSON.stringify(data));
configData = {
debug : false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: data.appid, // 必填,公众号的唯一标识
timestamp: data.timestamp, // 必填,生成签名的时间戳
nonceStr: data.nonceStr, // 必填,生成签名的随机串
signature: data.signature,// 必填,签名,见附录1
jsApiList : ['chooseImage','previewImage','uploadImage','downloadImage']
// 必填,需要使用的JS接口列表,所有JS接口列表见附录2
};
//step2:注入配置信息
wx.config(configData);
//step3:通过ready接口处理成功验证
wx.ready(function () {
// config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
// alert("执行ready方法");
// wx.checkJsApi({
// jsApiList: ['chooseImage'] // 需要检测的JS接口列表,所有JS接口列表见附录2,
// success: function(res) {
// // 以键值对的形式返回,可用的api值true,不可用为false
// // 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
// }
// });
//接口1:获取网络类型
wx.getNetworkType({
success: function (res) {
oldNet = res.networkType; // 返回网络类型2g,3g,4g,wifi
// alert("网络类型"+ oldNet);
}
});
// 拍照
// alert(document.querySelector('.head_portrait'));
document.querySelector('.head_portrait').onclick = function (){
$("#tips").html("点击上传图片");
var img1 = $('#chooseImage1');
//接口2:拍照或从手机相册中选图接口
wx.chooseImage({
count: 1, // 默认9
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'],//可以指定来源是相册还是相机,默认二者都有
success: function (res) {
var imgLocalId = res.localIds;//返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
// alert("imgLocalId=" + imgLocalId);
// alert(res.contentType);
if(!imgLocalId){
alert(2);
fnPicInfo('',imgLocalId,'choose','fb',JSON.stringify(res));
}
img1.attr('src',imgLocalId);
//选择图片之后询问是否确认上传
$("#isupload").css("display","block");
//不上传或者重新选择
$("#isupload_n").click(function(){
$("#isupload").css("display","none");
$("#tips").html("重新选择");
$("#chooseImage1").src("");
});
//确认上传
$("#isupload_y").click(function(){
$("#isupload").css("display","none");
//接口3:上传图片接口
wx.uploadImage({
localId: imgLocalId[0], // 需要上传的图片的本地ID,由chooseImage接口获得
isShowProgressTips: 1, // 默认为1,显示进度提示
success: function (res) {
if(res.serverId.indexOf("wxLocalResource://")>=0){
mui.alert('',"图片上传失败,请重新上传!",'');
return;
}
mediaId = res.serverId;
if(!mediaId){
fnPicInfo(mediaId,imgLocalId,'upload','fb',JSON.stringify(res));
}
img1.attr('src',imgLocalId);
mediaId1 = mediaId;
localId1 = imgLocalId;
//这里再写一个接口,将mediaId以及其他所需参数传到后台。回调函数中不需要写什么信息。
//准备请求的参数:班级编码、mediaId、上传类型
//注:班级编码和上传类型是根据项目所需上传的参数,大家可根据自己的项目定义,但是mediaId肯定是要上传的。
var requestdata = {
"classcode": classcode,
"mediaId": mediaId,
"uploadtype": uploadtype
}
alert(JSON.stringify(requestdata));
//调用图片上传接口
post_mediaId(requestdata);
},
fail: function(re){
wx.getNetworkType({
success: function (res) {
$('.hidden_word1').show();
newNet = res.networkType; // 返回网络类型2g,3g,4g,wifi
fnPicInfo('','','upload','fb',re);
}
});
}
});
});
},
fail: function(re){
alert(JSON.stringify(re));
wx.getNetworkType({
success: function (res) {
$('.hidden_word1').show();
newNet = res.networkType; // 返回网络类型2g,3g,4g,wifi
fnPicInfo('','','choose','fb',re);
}
});
}
});
};
})
}
});
// 向后台数据库传递照片的相关信息,根据实际需求看是否需要传递这些信息。
// function fnPicInfo(sId,lId,opType,businessType,remark) {
// remark+="||"+oldNet+"||"+newNet;
// $.ajax({
// url:Url+'maintainCommon/addWXMediaRecord',
// data:{
// serviceId:sId,
// localId:lId,
// clientType:navigator.userAgent,
// opType:opType,
// businessType:businessType,
// remark:remark
// }
// });
// }
/********************获取微信配置数据结束********************** */
//向后端传递mediaId的值,以便其能到微信服务器上下载图片到本地服务器。
var post_mediaId = function(requestData){
var server_IP = "服务器接口地址";
$.ajax({
url: server_IP,
type:"get",
//params意为参数,是自定义的,用以表明这是传给后台的数据。
data:{"params":JSON.stringify(requestData)},
contentType:"application/json; charset=utf-8",
//数据类型为jsonp,解决跨域问题。
dataType:"jsonp",
//自定义的jsonp回调函数名,默认为jQuery自动生成的随机函数
jsonpCallback:"success_jsonpCallback_select",
//传递给请求处理程序或页面的,用以获得jsonp回调函数名的参数名(默认为callback)
jsonp:"callbackparam",
//访问成功时的回调函数
success: function(respose){
alert(respose.message);
},
error: function(data){
alert("服务器连接失败");
}
});
}