前段時間在作一個微信的項目,遇到了一個上傳圖片的問題,花了一下午,解決了這個問題,而後把總結出來的代碼,分享了出來。javascript
最近又有一個圖片+語音的功能, 更是蛋疼, 本次採用的不是File文件上傳,而後轉碼(Base64)的形式解決的,而是使用微信的開放Js-SDK實現的。php
咱們.net苦逼的很,微信官方的Demo上,有php、java、nodejs以及python的示例代碼,就是沒有咱們.NET的,不知道大神們是否是都藏私了,如今大的不出來,作小的出來頂!d=====( ̄▽ ̄*)b html
(開句玩笑話,別當真,鄙人菜鳥一枚)java
閒話不聊, 立刻進入正題node
首先,咱們看看官方給的 Js-SDK https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CNpython
…… 文檔相信你們已經不陌生了,如何配置纔是問題, 接下來,給你們詳解 微信Js-SDK的調用(順帶給你們介紹下使用,老手能夠直接跳過前戲~ 1、2、3、四)web
第一步: 綁定域名 ajax
域名是指安全回調的域名 , 首先打開屬於你的公衆號,數據庫
點開 公衆號設置 - 功能設置 - 設置安全回調域名json
如圖:
設置好安全回調的域名之後, 下面進入第二步
第二步: 引入JS-SDK文件
官方已經給出了地址, 咱們直接在對應的頁面中 引用這個JS便可, 這個JS裏面的東西, 有微信已經封裝好的交互(深究的話,鄙人暫時無能爲力),直接引入便可
<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script> 相似這樣的
第三步: 經過config接口注入權限驗證配置
從這個時候開始, 咱們就須要配置調用 JS-SDK裏面的方法了;
全部須要使用JS-SDK的頁面必須先注入配置信息,不然將沒法調用(同一個url僅需調用一次,對於變化url的SPA的web app可在每次url變化時進行調用,目前Android微信客戶端不支持pushState的H5新特性,因此使用pushState來實現web app的頁面會致使簽名失敗,此問題會在Android6.2中修復)。
wx.config({
debug: true, // 開啓調試模式,調用的全部api的返回值會在客戶端alert出來,若要查看傳入的參數,能夠在pc端打開,參數信息會經過log打出,僅在pc端時纔會打印。
appId: '', // 必填,公衆號的惟一標識
timestamp: , // 必填,生成簽名的時間戳
nonceStr: '', // 必填,生成簽名的隨機串
signature: '',// 必填,簽名,見附錄1
jsApiList: [] // 必填,須要使用的JS接口列表, 可參考官方的目錄,添加之後,可使用該接口
});
下面是我在MVC裏配置的代碼附錄
控制器端: var appId = WeixinConfig.AppID; var nonceStr = Util.CreateNonce_str(); var timestamp = Util.CreateTimestamp(); var domain = System.Configuration.ConfigurationManager.AppSettings["Domain"]; var url = domain + Request.Url.PathAndQuery; var jsTickect = WeixinConfig.TokenHelper.GetJSTickect(); var string1 = ""; var signature = JSAPI.GetSignature(jsTickect, nonceStr, timestamp, url, out string1); ViewBag.JSAPI = new JSSDKModel { appId = appId, nonceStr = nonceStr, signature = signature, timestamp = timestamp, string1 = string1, };
@VIew頁面端: //配置微信 wx.config({ debug: false, appId: '@ViewBag.JSAPI.appId', // 必填,公衆號的惟一標識 timestamp: @ViewBag.JSAPI.timestamp, // 必填,生成簽名的時間戳 nonceStr: '@ViewBag.JSAPI.nonceStr', // 必填,生成簽名的隨機串 signature: '@ViewBag.JSAPI.signature',// 必填,簽名,見附錄1 jsApiList: [ 'chooseImage', 'previewImage', 'uploadImage', 'downloadImage', 'checkJsApi', 'startRecord', 'stopRecord', 'onVoiceRecordEnd', 'playVoice', 'pauseVoice', 'stopVoice', 'onVoicePlayEnd', 'uploadVoice', 'downloadVoice', 'getNetworkType' ] });
配置完成後,咱們進入第四步。
第四步: 接口驗證
官方給的驗證方法有 Ready 成功驗證 和 Error 失敗驗證,下面咱們看看代碼
wx.ready(function(){ // config信息驗證後會執行ready方法,全部接口調用都必須在config接口得到結果以後,config是一個客戶端的異步操做,因此若是須要在頁面加載時就調用相關接口,則須把相關接口放在ready函數中調用來確保正確執行。對於用戶觸發時才調用的接口,則能夠直接調用,不須要放在ready函數中。 }); wx.error(function(res){ // config信息驗證失敗會執行error函數,如簽名過時致使驗證失敗,具體錯誤信息能夠打開config的debug模式查看,也能夠在返回的res參數中查看,對於SPA能夠在這裏更新簽名。 });
再看看官方的接口調用說明:
全部接口經過wx對象(也可以使用jWeixin對象)來調用,參數是一個對象,除了每一個接口自己須要傳的參數以外,還有如下通用參數:
1.success:接口調用成功時執行的回調函數。
2.fail:接口調用失敗時執行的回調函數。
3.complete:接口調用完成時執行的回調函數,不管成功或失敗都會執行。
4.cancel:用戶點擊取消時的回調函數,僅部分有用戶取消操做的api纔會用到。
5.trigger: 監聽Menu中的按鈕點擊時觸發的方法,該方法僅支持Menu中的相關接口。
備註:不要嘗試在trigger中使用ajax異步請求修改本次分享的內容,由於客戶端分享操做是一個同步操做,這時候使用ajax的回包會尚未返回。
以上幾個函數都帶有一個參數,類型爲對象,其中除了每一個接口自己返回的數據以外,還有一個通用屬性errMsg,其值格式以下:
調用成功時:"xxx:ok" ,其中xxx爲調用的接口名
用戶取消時:"xxx:cancel",其中xxx爲調用的接口名
調用失敗時:其值爲具體錯誤信息
瞭解了以上信息之後,接下來就能夠進入咱們今天主要探討的一個問題了——微信的圖片接口和語音接口
咱們先看圖片接口,而後一一實驗
圖像接口 拍照或從手機相冊中選圖接口 wx.chooseImage({ count: 1, // 默認9 sizeType: ['original', 'compressed'], // 能夠指定是原圖仍是壓縮圖,默認兩者都有 sourceType: ['album', 'camera'], // 能夠指定來源是相冊仍是相機,默認兩者都有 success: function (res) { var localIds = res.localIds; // 返回選定照片的本地ID列表,localId能夠做爲img標籤的src屬性顯示圖片 } }); 預覽圖片接口 wx.previewImage({ current: '', // 當前顯示圖片的http連接 urls: [] // 須要預覽的圖片http連接列表 }); 上傳圖片接口 wx.uploadImage({ localId: '', // 須要上傳的圖片的本地ID,由chooseImage接口得到 isShowProgressTips: 1, // 默認爲1,顯示進度提示 success: function (res) { var serverId = res.serverId; // 返回圖片的服務器端ID } }); 備註:上傳圖片有效期3天,可用微信多媒體接口下載圖片到本身的服務器,此處得到的 serverId 即 media_id。 下載圖片接口 wx.downloadImage({ serverId: '', // 須要下載的圖片的服務器端ID,由uploadImage接口得到 isShowProgressTips: 1, // 默認爲1,顯示進度提示 success: function (res) { var localId = res.localId; // 返回圖片下載後的本地ID } });
關於圖片這邊,微信官方提供了四大接口,分別是選擇圖片的接口, 這個選擇相似於微信的圖片選擇,其實就是JS和微信的交互體驗,
選擇一個一個接口解釋
選擇圖片接口
chooseImage
:在選擇好圖片之後,圖片有一個localId返回,這個localId是微信緩存本地的一個ID,能夠直接使用這個localId,進行圖片顯示,
例如: $("#img").append('<img src="'+localId+'" style="width:50px; height:50px;"/> <br />'); 這樣的一句話,會在img標籤裏面,添加一張剛剛你選擇的圖片
預覽圖片接口
previewImage
:咱們注意到,這個預覽圖片的接口,有兩個參數, 一個是 current ,一個 urls , 這兩個參數是什麼意思呢? 首先, 不論是current 仍是 urls , 裏面的屬性值, 都是http:// 的 須要預覽的圖片的地址, current 表示在調用這個接口時
會顯示在第一頁的圖片, 而urls是表示,顯示的圖片集合 , 例如 :
var imgList = [ 'http://h.hiphotos.baidu.com/image/pic/item/4bed2e738bd4b31c70a6b76782d6277f9f2ff850.jpg', ]; wx.previewImage({ current: imgList[0], urls: imgList });
這樣,當點擊的事件觸發到這個方法後,會調用這個預覽接口,實現微信端看到的,點小圖, 看到大圖的效果
上傳圖片接口 uploadImage
上傳圖片這個接口,不少人有這樣的疑問(包括我第一次也是): 這個上傳的圖片在哪? 上傳到哪兒了去了? 咱們怎麼才能用這個圖片?
接下來,我感同身受的,一個一個解決這個問題。
疑: 上傳的圖片在哪裏? - 答 : 咱們注意到,以前在調用選擇圖片的接口的時候,有一個localId , 我解釋過,是微信端緩存本地的一個圖片編號,那麼,咱們聯想一下, 這個localId ,是否就是選擇獲得的localId了, 沒有錯, 就是這個localId, 那也就是說,
咱們想要使用上傳接口, 首先, 咱們得選擇圖片,才能進行相應的上傳操做。
疑: 上傳到哪兒去了? - 答: 當存在這個localId的時候,接口會找到相對應的圖片上,而後把該圖片,經過另外一個接口 : https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE
上傳至微信服務器的臨時素材裏面,固然,這個臨時素材只有三天,而且有空間限制, 具體參考微信公衆號裏面的接口權限一欄 。
疑: 咱們怎麼才能用這個圖片? - 答: 當調用圖片上傳接口成功, 會有一個返回值返回給咱們 ,類型是這樣的- {"type":"TYPE","media_id":"MEDIA_ID","created_at":123456789} ,
咱們須要注意到的有用的信息 就是這個 media_id ,這個media_id 表示咱們所上傳的素材, 在素材庫裏的位置, 當咱們想要使用它的時候, 只須要把這個 media_id 還有 token 請求到素材請求的地址, 就能夠獲得使用這個素材了。
下載圖片接口
downloadImage
其實在上一個接口裏面,稍稍已經提到過這個接口的使用, 沒有錯, 這個接口就是爲了下載臨時的素材,而後返回一個localId, 此時的localId, 是一個全新的, 表明的是下載到的圖片的localId, 使用的方法也仍是很簡單, 經過一個 Src 屬性便可使用
講到這裏, 不得不停一下, 咱們彷佛忽略了些什麼東西了,這些圖片只能用三天,那三天後怎麼辦? 還有啊, 這個素材庫有大小的限制, 不夠了怎麼辦?
怎麼辦? 問我好咯, 我來分享一個辦法—— 那就是轉存到咱們的服務器上, 微信已經替咱們壓縮了,咱們下載下來, 而後轉一下,送到咱們本身的服務器上,留個地址,在咱們的數據庫存儲一下,
一張能夠持久高清無碼圖, 就這樣被咱們收入「囊中」。
下面繼續貼代碼, 看不懂的私聊,
<a href="javascript:void(0);" onclick="javascript:chooseImage();">選擇圖片</a> <a href="javascript:void(0);" onclick="javascript:uploadImage();">圖片上傳</a> <a href="javascript:void(0);" onclick="javascript:downloadImage();">圖片下載</a> <a href="javascript:void(0);" onclick="javascript:previewImage();">圖片預覽</a> <div id="img">選擇的圖片</div> <div id="img2"> 下載的圖片</div> <script type="text/javascript"> wx.config({ debug: false, appId: '@ViewBag.JSAPI.appId', // 必填,公衆號的惟一標識 timestamp: @ViewBag.JSAPI.timestamp, // 必填,生成簽名的時間戳 nonceStr: '@ViewBag.JSAPI.nonceStr', // 必填,生成簽名的隨機串 signature: '@ViewBag.JSAPI.signature',// 必填,簽名,見附錄1 jsApiList: [ 'chooseImage', 'previewImage', 'uploadImage', 'downloadImage', 'checkJsApi', 'startRecord', 'stopRecord', 'onVoiceRecordEnd', 'playVoice', 'pauseVoice', 'stopVoice', 'onVoicePlayEnd', 'uploadVoice', 'downloadVoice', 'getNetworkType' ] }); wx.ready(function () { //返回網絡類型 wx.getNetworkType({ success: function (res) { var networkType = res.networkType; // 返回網絡類型2g,3g,4g,wifi jQuery(function() { $('#networkType').html(networkType); }); } }); }); // 5.1 拍照、本地選圖 var images = { localId: [], serverId: [] }; function chooseImage() { wx.chooseImage({ success: function (res) { images.localId = res.localIds; jQuery(function(){ $.each(res.localIds, function(i, n){ $("#img").append('<img src="'+n+'" style="width:50px; height:50px;"/> <br />'); }); }); } }); } // 5.2 圖片預覽 function previewImage() { var imgList = [ 'http://h.hiphotos.baidu.com/image/pic/item/4bed2e738bd4b31c70a6b76782d6277f9f2ff850.jpg', ]; wx.previewImage({ current: imgList[0], urls: imgList }); } // 5.3 上傳圖片 var i = 0, length =0; function uploadImage(){ if (images.localId.length == 0) { alert('請先使用選擇圖片按鈕'); return; } length= images.localId.length; upload(); } function upload() { wx.uploadImage({ localId: images.localId[i], success: function (res) { i++; //alert('已上傳:' + i + '/' + length); images.serverId.push(res.serverId); $("#img2").append('<img src="'+res.localId+'" style="width:50px; height:50px;"/ /> <br />'); downloadImage(res.serverId); if (i < length) { upload(); } }, fail: function (res) { alert(JSON.stringify(res)); } }); } //下載圖片 function downloadImage(serverId) { $.get("@Url.Action("WeixinDownloadImage","Home")",{mediaId:serverId},function(data){ alert(data); },"json") } wx.error(function (res) { alert(res.errMsg); }); </script>
注意到,我有定義一個全局變量, 能夠拓展到多圖上傳, 還有一個遞歸的上傳。 我先回去了,下班了,回家燒飯吃。
服務器端的,和語音的,下次再聊。