最近做項目,其中有個需求是在微信企業(yè)號上實現(xiàn)圖片上傳操作。一開始我就把重點放在了"移動端圖片上傳"而不是"微信圖片上傳",所以各種查資料,找插件,費(fèi)了好些功夫,實現(xiàn)起來也是各種困難。后來接觸到微信公眾號,稍微了解了一下,才知道利用微信公眾號或者現(xiàn)在已經(jīng)升級為企業(yè)微信的企業(yè)號的API,還是挺簡單的一個功能。下面我就詳細(xì)整理一下整個過程,做個筆記,一方面可以幫助到從未接觸過這個功能的同學(xué),一方面也方便自己日后翻閱。
1、首先進(jìn)入微信公眾平臺,查看JS-SDK開發(fā)文檔。https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115
2、開發(fā)文檔有詳細(xì)的步驟說明,但我還是根據(jù)我自己做的過程再稍微記錄一下。
第一步中的綁定安全域名可不做,不會影響功能的實現(xiàn)。我直接從第二部引進(jìn)js文件開始的。當(dāng)我們想要通過微信上傳圖片時,有兩種方法:一種是JS文件調(diào)用,一種是接口調(diào)用。這里用的是第一種,通過引進(jìn)JS文件,然后直接調(diào)用接口,不再使用URL等接口地址。
3、JS文件的引入:

4、通過config接口注入權(quán)限驗證配置:
這一步尤為重要。

1)在開發(fā)調(diào)試時,可將debug設(shè)為true,這樣可以看到返回的信息,方便調(diào)試bug。但在生產(chǎn)環(huán)境上,記得設(shè)為false。
2)appId是公眾號的唯一標(biāo)識。項目掛靠在哪個公眾號或者企業(yè)號,就去找相關(guān)負(fù)責(zé)人拿到appId,這個是固定的值,沒有改動,也不需要調(diào)用獲取。(服務(wù)器端,也就是后端開發(fā)人員去拿到這個值)
3)除了jsApiList的值需要前端人員根據(jù)需要自己寫以外,其他參數(shù)均需從后端獲取。最重要的是其中的簽名串,有其特定的生成規(guī)則,后端需要根據(jù)一定的規(guī)則,去調(diào)用微信的接口,獲取相關(guān)參數(shù)后生成簽名串,然后再將對應(yīng)的參數(shù)傳給前端。前端人員獲取到這些數(shù)據(jù)信息后,就可以完成配置信息的注入了。

4)可能會遇到的問題,當(dāng)在完成配置信息的注入后,實際開始調(diào)用所需接口時,出現(xiàn)報錯信息:invalid signature,permission denied,此時解決方法如下:
①首選檢查生成簽名串的算法(規(guī)則)是否正確;
② 其次需要特備注意的是:你在利用參數(shù)生成簽名的時候,要對所有待簽名參數(shù)按照字段名的 ASCII 碼從小到大排序(字典序)后,使用 URL 鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串 string1。這里需要注意的是所有參數(shù)名均為小寫字符。
③注意參數(shù)key值大小寫問題;
④生成簽名串時,需要前端人員給后端傳當(dāng)前頁面的URL,但不包括'#'hash后面的部分。注意前端傳值時的格式以及后端接收時的解析方式。
⑤確認(rèn) config 中的 appid 與用來獲取 jsapi_ticket 的 appid 一致。
⑥很重要的一點,確認(rèn)當(dāng)前訪問頁面的URL已經(jīng)被項目掛靠的公眾號或者企業(yè)號授權(quán)訪問。
更多錯誤信息及詳細(xì)解決方法,請參考:https://my.oschina.net/u/2308739/blog/371414
5)jsApiList中需要填寫的接口。(其實按現(xiàn)在這個操作,是不需要下載圖片接口的,因為下載這一操作是由后端實現(xiàn)的。)

5、配置信息注入并且成功校驗完之后,就可以在wx.ready接口下操作相關(guān)圖片接口了。

6、在wx.ready下調(diào)用圖片選擇、上傳、預(yù)覽等接口時,直接按照操作的順序調(diào)用即可。但其中涉及到的一個問題是,如果前端沒有自己的后臺,需要和另外一臺服務(wù)器上的后臺進(jìn)行交互,此時在圖片上傳時就需要調(diào)用后端接口,將圖片上傳后生成的文件流,最終得到的mediaId傳給后端,然后讓后端去微信服務(wù)器上下載剛剛上傳的圖片。
7、話不多說,直接上全部代碼。
/** ******************獲取微信配置數(shù)據(jù)********************** */
var Url = ""; //服務(wù)器接口路徑
var oldNet = '';
var newNet = '';//記錄網(wǎng)絡(luò)狀態(tài)
var configData;
$.ajax({
url: Url + '/GetSignatureInfo',//后端接口名,可自定義。
// data: {pageUrl: location.href.split('#')[0]},
type:"get",
//params意為參數(shù),是自定義的,用以表明這是傳給后臺的數(shù)據(jù)。
data:{"params":location.href.split('#')[0]},
contentType:"application/json; charset=utf-8",
//數(shù)據(jù)類型為jsonp,解決跨域問題。
dataType:"jsonp",
//自定義的jsonp回調(diào)函數(shù)名,默認(rèn)為jQuery自動生成的隨機(jī)函數(shù)
jsonpCallback:"success_jsonpCallback_select",
//傳遞給請求處理程序或頁面的,用以獲得jsonp回調(diào)函數(shù)名的參數(shù)名(默認(rèn)為callback)
jsonp:"callbackparam",
success: function (data) {
// alert(JSON.stringify(data));
configData = {
debug : false, // 開啟調(diào)試模式,調(diào)用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數(shù),可以在pc端打開,參數(shù)信息會通過log打出,僅在pc端時才會打印。
appId: data.appid, // 必填,公眾號的唯一標(biāo)識
timestamp: data.timestamp, // 必填,生成簽名的時間戳
nonceStr: data.nonceStr, // 必填,生成簽名的隨機(jī)串
signature: data.signature,// 必填,簽名,見附錄1
jsApiList : ['chooseImage','previewImage','uploadImage','downloadImage']
// 必填,需要使用的JS接口列表,所有JS接口列表見附錄2
};
//step2:注入配置信息
wx.config(configData);
//step3:通過ready接口處理成功驗證
wx.ready(function () {
// config信息驗證后會執(zhí)行ready方法,所有接口調(diào)用都必須在config接口獲得結(jié)果之后,config是一個客戶端的異步操作,所以如果需要在頁面加載時就調(diào)用相關(guān)接口,則須把相關(guān)接口放在ready函數(shù)中調(diào)用來確保正確執(zhí)行。對于用戶觸發(fā)時才調(diào)用的接口,則可以直接調(diào)用,不需要放在ready函數(shù)中。
// alert("執(zhí)行ready方法");
// wx.checkJsApi({
// jsApiList: ['chooseImage'] // 需要檢測的JS接口列表,所有JS接口列表見附錄2,
// success: function(res) {
// // 以鍵值對的形式返回,可用的api值true,不可用為false
// // 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
// }
// });
//接口1:獲取網(wǎng)絡(luò)類型
wx.getNetworkType({
success: function (res) {
oldNet = res.networkType; // 返回網(wǎng)絡(luò)類型2g,3g,4g,wifi
// alert("網(wǎng)絡(luò)類型"+ oldNet);
}
});
// 拍照
// alert(document.querySelector('.head_portrait'));
document.querySelector('.head_portrait').onclick = function (){
$("#tips").html("點擊上傳圖片");
var img1 = $('#chooseImage1');
//接口2:拍照或從手機(jī)相冊中選圖接口
wx.chooseImage({
count: 1, // 默認(rèn)9
sizeType: ['original', 'compressed'], // 可以指定是原圖還是壓縮圖,默認(rèn)二者都有
sourceType: ['album', 'camera'],//可以指定來源是相冊還是相機(jī),默認(rèn)二者都有
success: function (res) {
var imgLocalId = res.localIds;//返回選定照片的本地ID列表,localId可以作為img標(biāo)簽的src屬性顯示圖片
// alert("imgLocalId=" + imgLocalId);
// alert(res.contentType);
if(!imgLocalId){
alert(2);
fnPicInfo('',imgLocalId,'choose','fb',JSON.stringify(res));
}
img1.attr('src',imgLocalId);
//選擇圖片之后詢問是否確認(rèn)上傳
$("#isupload").css("display","block");
//不上傳或者重新選擇
$("#isupload_n").click(function(){
$("#isupload").css("display","none");
$("#tips").html("重新選擇");
$("#chooseImage1").src("");
});
//確認(rèn)上傳
$("#isupload_y").click(function(){
$("#isupload").css("display","none");
//接口3:上傳圖片接口
wx.uploadImage({
localId: imgLocalId[0], // 需要上傳的圖片的本地ID,由chooseImage接口獲得
isShowProgressTips: 1, // 默認(rèn)為1,顯示進(jìn)度提示
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以及其他所需參數(shù)傳到后臺?;卣{(diào)函數(shù)中不需要寫什么信息。
//準(zhǔn)備請求的參數(shù):班級編碼、mediaId、上傳類型
//注:班級編碼和上傳類型是根據(jù)項目所需上傳的參數(shù),大家可根據(jù)自己的項目定義,但是mediaId肯定是要上傳的。
var requestdata = {
"classcode": classcode,
"mediaId": mediaId,
"uploadtype": uploadtype
}
alert(JSON.stringify(requestdata));
//調(diào)用圖片上傳接口
post_mediaId(requestdata);
},
fail: function(re){
wx.getNetworkType({
success: function (res) {
$('.hidden_word1').show();
newNet = res.networkType; // 返回網(wǎng)絡(luò)類型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; // 返回網(wǎng)絡(luò)類型2g,3g,4g,wifi
fnPicInfo('','','choose','fb',re);
}
});
}
});
};
})
}
});
// 向后臺數(shù)據(jù)庫傳遞照片的相關(guān)信息,根據(jù)實際需求看是否需要傳遞這些信息。
// 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
// }
// });
// }
/********************獲取微信配置數(shù)據(jù)結(jié)束********************** */
//向后端傳遞mediaId的值,以便其能到微信服務(wù)器上下載圖片到本地服務(wù)器。
var post_mediaId = function(requestData){
var server_IP = "服務(wù)器接口地址";
$.ajax({
url: server_IP,
type:"get",
//params意為參數(shù),是自定義的,用以表明這是傳給后臺的數(shù)據(jù)。
data:{"params":JSON.stringify(requestData)},
contentType:"application/json; charset=utf-8",
//數(shù)據(jù)類型為jsonp,解決跨域問題。
dataType:"jsonp",
//自定義的jsonp回調(diào)函數(shù)名,默認(rèn)為jQuery自動生成的隨機(jī)函數(shù)
jsonpCallback:"success_jsonpCallback_select",
//傳遞給請求處理程序或頁面的,用以獲得jsonp回調(diào)函數(shù)名的參數(shù)名(默認(rèn)為callback)
jsonp:"callbackparam",
//訪問成功時的回調(diào)函數(shù)
success: function(respose){
alert(respose.message);
},
error: function(data){
alert("服務(wù)器連接失敗");
}
});
}