由於現在手頭的項目中有一個上傳證件照認證的功能(手機端),之前的思路是直接點擊上傳,然后直接將圖片上傳到服務器去,這篇文章有講到(http://www.cnblogs.com/it-cen/p/4535219.html),但在微信里打開網頁去上傳,速度並不快,而且,假如我上傳一張2M大的圖片,也沒有對其進行壓縮處理,這樣很影響上傳和下載的速度。
所以,我這里借助微信JSSDK的圖像接口對其進行開發實現圖片上傳的功能,為何我選擇此接口?第一,目前的項目是在微信中打開的網頁,利用此接口,性能肯定是好一點的啦,畢竟是微信自己的東西;第二,用此接口,開發效率更高嘛;第三,最重要的一點,就是它能對圖片進行壓縮,假如一張2M的圖片,通過微信圖片上傳接口可以將圖片壓縮成幾百K的大小,這對網站的性能是很有幫助的。
一、我的思路是:
先調用“拍照或從手機相冊選擇圖片接口”—>選擇成功圖片后—>調用“上傳圖片接口”—>上傳成功后(也就是圖片上傳到了微信服務器上)—>調用“下載圖片接口”—>將圖片下載到自己的服務器存儲。
二、JSSDK的使用步驟
1、概述
微信JS-SDK是微信公眾平台面向網頁開發者提供的基於微信內的網頁開發工具包。
通過使用微信JS-SDK,網頁開發者可借助微信高效地使用拍照、選圖、語音、位置等手機系統的能力,同時可以直接使用微信分享、掃一掃、卡券、支付等微信特有的能力,為微信用戶提供更優質的網頁體驗。
2、使用步驟
步驟一:綁定域名
先登錄微信公眾平台進入“公眾號設置”的“功能設置”里填寫“JS接口安全域名”。
備注:登錄后可在“開發者中心”查看對應的接口權限。
步驟二:引入JS文件
在需要調用JS接口的頁面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.0.0.js
步驟三:通過config接口注入權限驗證配置
所有需要使用JS-SDK的頁面必須先注入配置信息,否則將無法調用(同一個url僅需調用一次,對於變化url的SPA的web app可在每次url變化時進行調用)
1 wx.config({ 2 debug: true, // 開啟調試模式,調用的所有api的返回值會在客戶端alert出來,若要查看傳入的參數,可以在pc端打開,參數信息會通過log打出,僅在pc端時才會打印。 3 appId: '', // 必填,公眾號的唯一標識 4 timestamp: , // 必填,生成簽名的時間戳 5 nonceStr: '', // 必填,生成簽名的隨機串 6 signature: '',// 必填,簽名,見附錄1 7 jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表見附錄2 8 });
步驟四:通過ready接口處理成功驗證
1 wx.ready(function(){ 2 3 // config信息驗證后會執行ready方法,所有接口調用都必須在config接口獲得結果之后,config是一個客戶端的異步操作,所以如果需要在頁面加載時就調用相關接口,則須把相關接口放在ready函數中調用來確保正確執行。對於用戶觸發時才調用的接口,則可以直接調用,不需要放在ready函數中。 4 });
步驟五:通過error接口處理失敗驗證
1 wx.error(function(res){ 2 3 // config信息驗證失敗會執行error函數,如簽名過期導致驗證失敗,具體錯誤信息可以打開config的debug模式查看,也可以在返回的res參數中查看,對於SPA可以在這里更新簽名。 4 5 });
接口調用說明
所有接口通過wx對象(也可使用jWeixin對象)來調用,參數是一個對象,除了每個接口本身需要傳的參數之外,還有以下通用參數:
- success:接口調用成功時執行的回調函數。
- fail:接口調用失敗時執行的回調函數。
- complete:接口調用完成時執行的回調函數,無論成功或失敗都會執行。
- cancel:用戶點擊取消時的回調函數,僅部分有用戶取消操作的api才會用到。
- trigger: 監聽Menu中的按鈕點擊時觸發的方法,該方法僅支持Menu中的相關接口。
備注:不要嘗試在trigger中使用ajax異步請求修改本次分享的內容,因為客戶端分享操作是一個同步操作,這時候使用ajax的回包會還沒有返回。
以上幾個函數都帶有一個參數,類型為對象,其中除了每個接口本身返回的數據之外,還有一個通用屬性errMsg,其值格式如下:
- 調用成功時:"xxx:ok" ,其中xxx為調用的接口名
- 用戶取消時:"xxx:cancel",其中xxx為調用的接口名
- 調用失敗時:其值為具體錯誤信息
三、開發及代碼分析詳解(用的是CI框架,只要是MVC模式都可以)
1、先在服務器端取到:公眾號的唯一標識appId、生成簽名的時間戳timestamp、生成簽名的隨機串nonceStr、簽名signature。
wx_upload.php

1 <?php 2 class wx_upload extends xx_Controller { 3 public function __construct() { 4 parent::__construct(); 5 } 6 7 public function wxUploadImg() { 8 //在模板里引入jssdk的js文件 9 $this->addResLink('http://res.wx.qq.com/open/js/jweixin-1.0.0.js'); 10 //取得:公眾號的唯一標識appId、生成簽名的時間戳timestamp、生成簽名的隨機串nonceStr、簽名signature這些值,並以json形式傳到模板頁面 11 $this->smartyData['wxJsApi'] = json_encode(array('signPackage' => $this->model->weixin->signPackage())); 12 }
WeixinModel.php

1 <?php 2 class WxModel extends ModelBase{ 3 public $appId; 4 public $appSecret; 5 public $token; 6 7 public function __construct() { 8 parent::__construct(); 9 10 //審核通過的移動應用所給的AppID和AppSecret 11 $this->appId = 'wx0000000000000000'; 12 $this->appSecret = '00000000000000000000000000000'; 13 $this->token = '00000000'; 14 } 15 16 /** 17 * 獲取jssdk所需參數的所有值 18 * @return array 19 */ 20 public function signPackage() { 21 $protocol = (!empty($_SERVER['HTTPS'] && $_SERVER['HTTPS'] == 'off' || $_SERVER['port'] == 443)) ? 'https://' : 'http://'; 22 //當前網頁的URL 23 $url = "$protocol$_SERVER['host']$_SERVER['REQUEST_URI']"; 24 //生成簽名的時間戳 25 $timestamp = time(); 26 //生成簽名的隨機串 27 $nonceStr = $this->createNonceStr(); 28 //獲取公眾號用於調用微信JS接口的臨時票據 29 $jsApiTicket = $this->getJsApiTicket(); 30 //對所有待簽名參數按照字段名的ASCII 碼從小到大排序(字典序)后, 31 //使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串$str。 32 //這里需要注意的是所有參數名均為小寫字符 33 $str = "jsapi_ticket=$jsApiTicket&noncestr=$nonceStr×tamp=$timestamp&url=$url"; 34 //對$str進行sha1簽名,得到signature: 35 $signature = sha1($str); 36 $signPackage = array( 37 "appId" => $this->AppId, 38 "nonceStr" => $nonceStr, 39 "timestamp" => $timestamp, 40 "url" => $url, 41 "signature" => $signature, 42 "rawString" => $string 43 ); 44 return $signPackage; 45 } 46 47 /** 48 * 創建簽名的隨機字符串 49 * @param int $length 字符串長度 50 * @return string 隨機字符串 51 */ 52 private function createNonceStr($length == 16) { 53 $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; 54 $str = ''; 55 for ($i=0; $i < $length; $i++) { 56 $str .= substr(mt_rand(0, strlen($chars)), 1); 57 } 58 return $str; 59 } 60 61 /** 62 * 獲取公眾號用於調用微信JS接口的臨時票據 63 * @return string 64 */ 65 private function getJsApiTicket() { 66 //先查看redis里是否存了jsapi_ticket此值,假如有,就直接返回 67 $jsApiTicket = $this->library->redisCache->get('weixin:ticket'); 68 if (!$jsApiTicket) { 69 //先獲取access_token(公眾號的全局唯一票據) 70 $accessToken = $this->getApiToken(); 71 //通過access_token 采用http GET方式請求獲得jsapi_ticket 72 $result = $this->callApi("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=$accessToken&type=jsapi"); 73 //得到了jsapi_ticket 74 $jsApiTicket = $result['ticket']; 75 //將jsapi_ticket緩存到redis里面,下次就不用再請求去取了 76 $expire = max(1, intval($result['expire']) - 60); 77 $this->library->redisCache->set('weixin:ticket', $jsApiTicket, $expire); 78 } 79 return $jsApiTicket; 80 } 81 82 /** 83 * 獲取眾號的全局唯一票據access_token 84 * @param boolean $forceRefresh 是否強制刷新 85 * @return string 返回access_token 86 */ 87 private function getApiToken($forceRefresh = false) { 88 //先查看redis是否存了accessToken,如果有了,就不用再去微信server去請求了(提高效率) 89 $accessToken = $this->library->redisCache->get('weixin:accessToken'); 90 //強制刷新accessToken或者accessToken為空時就去請求accessToken 91 if ($forceRefresh || empty($accessToken)) { 92 //請求得到accessToken 93 $result = $this->callApi("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appId}&secret={$this->appSecret}"); 94 $accessToken = $result['access_token']; 95 $expire = max(1, intval($result['expire']) - 60); 96 //將其存進redis里面去 97 $this->library->redisCache->set('weixin:accessToken', $accessToken, $expire); 98 } 99 return $accessToken; 100 }
這里要補充一些 JS-SDK使用權限簽名算法 的思路和注意點(這里我直接復制官網文檔給大家看看)
jsapi_ticket
生成簽名之前必須先了解一下jsapi_ticket,jsapi_ticket是公眾號用於調用微信JS接口的臨時票據。正常情況下,jsapi_ticket的有效期為7200秒,通過access_token來獲取。由於獲取jsapi_ticket的api調用次數非常有限,頻繁刷新jsapi_ticket會導致api調用受限,影響自身業務,開發者必須在自己的服務全局緩存jsapi_ticket 。
1、獲取access_token(有效期7200秒,開發者必須在自己的服務全局緩存access_token)
2、用第一步拿到的access_token 采用http GET方式請求獲得jsapi_ticket(有效期7200秒,開發者必須在自己的服務全局緩存jsapi_ticket)
https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi
成功返回如下JSON:
1 { 2 "errcode":0, 3 "errmsg":"ok", 4 "ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA", 5 "expires_in":7200 6 }
獲得jsapi_ticket之后,就可以生成JS-SDK權限驗證的簽名了。
簽名算法
簽名生成規則如下:參與簽名的字段包括noncestr(隨機字符串), 有效的jsapi_ticket, timestamp(時間戳), url(當前網頁的URL,不包含#及其后面部分) 。對所有待簽名參數按照字段名的ASCII 碼從小到大排序(字典序)后,使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串string1。這里需要注意的是所有參數名均為小寫字符。對string1作sha1加密,字段名和字段值都采用原始值,不進行URL 轉義。
即signature=sha1(string1)。 示例:
-
- noncestr=Wm3WZYTPz0wzccnW
- jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg
- timestamp=1414587457
- url=http://mp.weixin.qq.com?params=value
步驟1. 對所有待簽名參數按照字段名的ASCII 碼從小到大排序(字典序)后,使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串string1:
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW×tamp=1414587457&url=http://mp.weixin.qq.com?params=value
步驟2. 對string1進行sha1簽名,得到signature:
0f9de62fce790f9a083d5c99e95740ceb90c27ed
注意事項
1.簽名用的noncestr和timestamp必須與wx.config中的nonceStr和timestamp相同。
2.簽名用的url必須是調用JS接口頁面的完整URL。
3.出於安全考慮,開發者必須在服務器端實現簽名的邏輯。
2、取到我們所需要的值后,就在js文件里面開始使用了
uploadImg.tpl
<script> $(function(){ $.util.wxMenuImage('{$wxJsApi|default:""}') }); </script>
uploadImg.js

1 if(typeof($util)=='undefined')$util={}; 2 3 $.util.wxMenuImage = function(json) { 4 if (json.length == 0) return; 5 //解析json變成js對象 6 wxJsApi = JSON.parse(json); 7 8 //通過config接口注入權限驗證配置 9 wx.config({ 10 debug: false, //開啟調試模式,調用的所有api的返回值會在客戶端alert出來 11 appId: wxJsApi.signPackage.appId, //公眾號的唯一標識 12 timestamp: wxJsApi.signPackage.timestamp, //生成簽名的時間戳 13 nonceStr: wxJsApi.signPackage.nonceStr, //生成簽名的隨機串 14 signature: wxJsApi.signPackage.signature, //簽名 15 jsApiList: ['chooseImage', 'uploadImage'] //需要使用的JS接口列表 這里我用了選擇圖片和上傳圖片接口 16 }); 17 18 //通過ready接口處理成功驗證,config信息驗證后會執行ready方法,所有接口調用都必須在config接口獲得結果之后 19 wx.ready(function(){ 20 //得到上傳圖片按鈕 21 document.querySelector('#uploadImage').onclick = function() { 22 var images = {localId:[],serverId:[]}; 23 //調用 拍照或從手機相冊中選圖接口 24 wx.chooseImage({ 25 success: function(res) { 26 if (res.localIds.length != 1) { 27 alert('只能上傳一張圖片'); 28 return; 29 } 30 //返回選定照片的本地ID列表 31 iamges.localId = res.localIds; 32 images.serverId = []; 33 //上傳圖片函數 34 function upload() { 35 //調用上傳圖片接口 36 wx.uploadImage({ 37 localId: images.localId[0], // 需要上傳的圖片的本地ID,由chooseImage接口獲得 38 isShowProcess: 1, // 默認為1,顯示進度提示 39 success: function(res) { 40 //返回圖片的服務器端ID res.serverId,然后調用wxImgCallback函數進行下載圖片操作 41 wxImgCallback(res.serverId); 42 }, 43 fail: function(res) { 44 alert('上傳失敗'); 45 } 46 }); 47 } 48 upload(); 49 } 50 }); 51 } 52 }); 53 } 54 55 56 function wxImgCallback(serverId) { 57 //將serverId傳給wx_upload.php的upload方法 58 var url = 'wx_upload/upload/'+serverId; 59 $.getJSON(url, function(data){ 60 if (data.code == 0) { 61 alert(data.msg); 62 } else if (data.code == 1) { 63 //存儲到服務器成功后的處理 64 // 65 } 66 }); 67 }
3、圖片上傳完成后會返回一個serverId,然后通過這個來下載圖片到本地服務器
這里先補充下如何調用下載圖片接口(我直接復制官方文檔的說明了)
公眾號可調用本接口來獲取多媒體文件。請注意,視頻文件不支持下載,調用該接口需http協議。
接口調用請求說明
http請求方式: GET http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID
參數說明
參數 | 是否必須 | 說明 |
---|---|---|
access_token | 是 | 調用接口憑證 |
media_id | 是 | 媒體文件ID |
返回說明
正確情況下的返回HTTP頭如下:
HTTP/1.1 200 OK Connection: close Content-Type: image/jpeg Content-disposition: attachment; filename="MEDIA_ID.jpg" Date: Sun, 06 Jan 2013 10:20:18 GMT Cache-Control: no-cache, must-revalidate Content-Length: 339721 curl -G "http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID"
錯誤情況下的返回JSON數據包示例如下(示例為無效媒體ID錯誤)::
{"errcode":40007,"errmsg":"invalid media_id"}
接下來看自己寫的代碼
wx_upload.php

1 /*********************圖片下載到本地服務器****************************************/ 2 //從微信服務器讀取圖片,然后下載到本地服務器 3 public function upload($media_id) { 4 //圖片文件名 5 $fileName = md5($this->wxId."/$media_id"); 6 //調用下載圖片接口,返回路徑 7 $path = $this->weixin->wxDownImg($media_id, sys_get_temp_dir()."$fileName"); 8 if ($path != false) { 9 //將圖片的路徑插入數據庫去存儲 10 if ($this->model->weixin->updateByWxid($this->wxId, array('img_path'=>$path))) { 11 $this->output->_display(json_encode( 12 array( 13 'code'=>1, 14 'msg'=>'上傳成功', 15 'fileUrl' =>$path; 16 ) 17 )); 18 } else { 19 $this->output->_display(json_encode2(array('code'=>0,'msg' => '上傳失敗','err'=>'1'))); 20 } 21 } else { 22 $this->output->_display(json_encode2(array('code'=>0,'msg' => '上傳失敗','err'=>'2'))); 23 } 24 25 }
WeixinModel.php

1 //從微信服務器端下載圖片到本地服務器 2 public function wxDownImg($media_id, $path) { 3 //調用 多媒體文件下載接口 4 $url = "https://api.weixin.qq.com/cgi-bin/media/get?access_token={$this->model->weixin->_getApiToken()}&media_id=$media_id"; 5 //用curl請求,返回文件資源和curl句柄的信息 6 $info = $this->curl_request($url); 7 //文件類型 8 $types = array('image/bmp'=>'.bmp', 'image/gif'=>'.gif', 'image/jpeg'=>'.jpg', 'image/png'=>'.png'); 9 //判斷響應首部里的的content-type的值是否是這四種圖片類型 10 if (isset($types[$info['header']['content_type']])) { 11 //文件的uri 12 $path = $path.$types[$info['header']['content_type']]; 13 } else { 14 return false; 15 } 16 17 //將資源寫入文件里 18 if ($this->saveFile($path, $info['body'])) { 19 //將文件保存在本地目錄 20 $imgPath = rtrim(base_url(), '/').'/img'.date('Ymd').'/'.md5($this->controller->wxId.$media_id).$types[$info['header'['content_type']]]; 21 if (!is_dir($imgPath)) { 22 if(mkdir($imgPath)) { 23 if (false !== rename($path, $imgPath) { 24 return $imgPath; 25 } 26 } 27 } 28 return $path; 29 } 30 31 return false; 32 33 } 34 35 /** 36 * curl請求資源 37 * @param string $url 請求url 38 * @return array 39 */ 40 private function curl_request($url = '') { 41 if ($url == '') return; 42 $ch = curl_init(); 43 //這里返回響應報文時,只要body的內容,其他的都不要 44 curl_setopt($ch, CURLOPT_HEADER, 0); 45 curl_setopt($ch, CURLOPT_NOBODY, 0); 46 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 47 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 48 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 49 $package = curl_exec($ch); 50 //獲取curl連接句柄的信息 51 $httpInfo = curl_getinfo($ch); 52 curl_close($ch); 53 54 $info = array_merge(array($package), array($httpInfo)); 55 56 return $info; 57 58 } 59 60 /** 61 * 將資源寫入文件 62 * @param string 資源uri 63 * @param source 資源 64 * @return boolean 65 */ 66 private function saveFile($path, $fileContent) { 67 $fp = fopen($path, 'w'); 68 if (false !== $localFile) { 69 if (false !== fwrite($fp, $fileContent)) { 70 fclose($fp); 71 return true; 72 } 73 } 74 return false; 75 }
到這里,已經完成了:
先調用“拍照或從手機相冊選擇圖片接口”—>選擇成功圖片后—>調用“上傳圖片接口”—>上傳成功后(也就是圖片上傳到了微信服務器上)—>調用“下載圖片接口”—>將圖片下載到自己的服務器存儲。
這一思路的實現。我們用到了微信的選擇圖片接口、上傳圖片接口和下載媒體資源接口。
下面我附上這一接口開發的全部代碼:

1 <?php 2 class wx_upload extends xx_Controller { 3 public function __construct() { 4 parent::__construct(); 5 } 6 7 public function wxUploadImg() { 8 //在模板里引入jssdk的js文件 9 $this->addResLink('http://res.wx.qq.com/open/js/jweixin-1.0.0.js'); 10 //取得:公眾號的唯一標識appId、生成簽名的時間戳timestamp、生成簽名的隨機串nonceStr、簽名signature這些值,並以json形式傳到模板頁面 11 $this->smartyData['wxJsApi'] = json_encode(array('signPackage' => $this->model->weixin->signPackage())); 12 } 13 14 /*********************圖片下載到本地服務器****************************************/ 15 //從微信服務器讀取圖片,然后下載到本地服務器 16 public function upload($media_id) { 17 //圖片文件名 18 $fileName = md5($this->wxId."/$media_id"); 19 //調用下載圖片接口,返回路徑 20 $path = $this->weixin->wxDownImg($media_id, sys_get_temp_dir()."$fileName"); 21 if ($path != false) { 22 //將圖片的路徑插入數據庫去存儲 23 if ($this->model->weixin->updateByWxid($this->wxId, array('img_path'=>$path))) { 24 $this->output->_display(json_encode( 25 array( 26 'code'=>1, 27 'msg'=>'上傳成功', 28 'fileUrl' =>$path; 29 ) 30 )); 31 } else { 32 $this->output->_display(json_encode2(array('code'=>0,'msg' => '上傳失敗','err'=>'1'))); 33 } 34 } else { 35 $this->output->_display(json_encode2(array('code'=>0,'msg' => '上傳失敗','err'=>'2'))); 36 } 37 38 } 39 } 40 41 42 43 44 45 46 47 48 49 50 51 ?>

1 <?php 2 class WxModel extends ModelBase{ 3 public $appId; 4 public $appSecret; 5 public $token; 6 7 public function __construct() { 8 parent::__construct(); 9 10 //審核通過的移動應用所給的AppID和AppSecret 11 $this->appId = 'wx0000000000000000'; 12 $this->appSecret = '00000000000000000000000000000'; 13 $this->token = '00000000'; 14 } 15 16 /** 17 * 獲取jssdk所需參數的所有值 18 * @return array 19 */ 20 public function signPackage() { 21 $protocol = (!empty($_SERVER['HTTPS'] && $_SERVER['HTTPS'] == 'off' || $_SERVER['port'] == 443)) ? 'https://' : 'http://'; 22 //當前網頁的URL 23 $url = "$protocol$_SERVER['host']$_SERVER['REQUEST_URI']"; 24 //生成簽名的時間戳 25 $timestamp = time(); 26 //生成簽名的隨機串 27 $nonceStr = $this->createNonceStr(); 28 //獲取公眾號用於調用微信JS接口的臨時票據 29 $jsApiTicket = $this->getJsApiTicket(); 30 //對所有待簽名參數按照字段名的ASCII 碼從小到大排序(字典序)后, 31 //使用URL鍵值對的格式(即key1=value1&key2=value2…)拼接成字符串$str。 32 //這里需要注意的是所有參數名均為小寫字符 33 $str = "jsapi_ticket=$jsApiTicket&noncestr=$nonceStr×tamp=$timestamp&url=$url"; 34 //對$str進行sha1簽名,得到signature: 35 $signature = sha1($str); 36 $signPackage = array( 37 "appId" => $this->AppId, 38 "nonceStr" => $nonceStr, 39 "timestamp" => $timestamp, 40 "url" => $url, 41 "signature" => $signature, 42 "rawString" => $string 43 ); 44 return $signPackage; 45 } 46 47 /** 48 * 創建簽名的隨機字符串 49 * @param int $length 字符串長度 50 * @return string 隨機字符串 51 */ 52 private function createNonceStr($length == 16) { 53 $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; 54 $str = ''; 55 for ($i=0; $i < $length; $i++) { 56 $str .= substr(mt_rand(0, strlen($chars)), 1); 57 } 58 return $str; 59 } 60 61 /** 62 * 獲取公眾號用於調用微信JS接口的臨時票據 63 * @return string 64 */ 65 private function getJsApiTicket() { 66 //先查看redis里是否存了jsapi_ticket此值,假如有,就直接返回 67 $jsApiTicket = $this->library->redisCache->get('weixin:ticket'); 68 if (!$jsApiTicket) { 69 //先獲取access_token(公眾號的全局唯一票據) 70 $accessToken = $this->getApiToken(); 71 //通過access_token 采用http GET方式請求獲得jsapi_ticket 72 $result = $this->callApi("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=$accessToken&type=jsapi"); 73 //得到了jsapi_ticket 74 $jsApiTicket = $result['ticket']; 75 //將jsapi_ticket緩存到redis里面,下次就不用再請求去取了 76 $expire = max(1, intval($result['expire']) - 60); 77 $this->library->redisCache->set('weixin:ticket', $jsApiTicket, $expire); 78 } 79 return $jsApiTicket; 80 } 81 82 /** 83 * 獲取眾號的全局唯一票據access_token 84 * @param boolean $forceRefresh 是否強制刷新 85 * @return string 返回access_token 86 */ 87 private function getApiToken($forceRefresh = false) { 88 //先查看redis是否存了accessToken,如果有了,就不用再去微信server去請求了(提高效率) 89 $accessToken = $this->library->redisCache->get('weixin:accessToken'); 90 //強制刷新accessToken或者accessToken為空時就去請求accessToken 91 if ($forceRefresh || empty($accessToken)) { 92 //請求得到accessToken 93 $result = $this->callApi("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appId}&secret={$this->appSecret}"); 94 $accessToken = $result['access_token']; 95 $expire = max(1, intval($result['expire']) - 60); 96 //將其存進redis里面去 97 $this->library->redisCache->set('weixin:accessToken', $accessToken, $expire); 98 } 99 return $accessToken; 100 } 101 102 //從微信服務器端下載圖片到本地服務器 103 public function wxDownImg($media_id, $path) { 104 //調用 多媒體文件下載接口 105 $url = "https://api.weixin.qq.com/cgi-bin/media/get?access_token={$this->model->weixin->_getApiToken()}&media_id=$media_id"; 106 //用curl請求,返回文件資源和curl句柄的信息 107 $info = $this->curl_request($url); 108 //文件類型 109 $types = array('image/bmp'=>'.bmp', 'image/gif'=>'.gif', 'image/jpeg'=>'.jpg', 'image/png'=>'.png'); 110 //判斷響應首部里的的content-type的值是否是這四種圖片類型 111 if (isset($types[$info['header']['content_type']])) { 112 //文件的uri 113 $path = $path.$types[$info['header']['content_type']]; 114 } else { 115 return false; 116 } 117 118 //將資源寫入文件里 119 if ($this->saveFile($path, $info['body'])) { 120 //將文件保存在本地目錄 121 $imgPath = rtrim(base_url(), '/').'/img'.date('Ymd').'/'.md5($this->controller->wxId.$media_id).$types[$info['header'['content_type']]]; 122 if (!is_dir($imgPath)) { 123 if(mkdir($imgPath)) { 124 if (false !== rename($path, $imgPath) { 125 return $imgPath; 126 } 127 } 128 } 129 return $path; 130 } 131 132 return false; 133 134 } 135 136 /** 137 * curl請求資源 138 * @param string $url 請求url 139 * @return array 140 */ 141 private function curl_request($url = '') { 142 if ($url == '') return; 143 $ch = curl_init(); 144 //這里返回響應報文時,只要body的內容,其他的都不要 145 curl_setopt($ch, CURLOPT_HEADER, 0); 146 curl_setopt($ch, CURLOPT_NOBODY, 0); 147 curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); 148 curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); 149 curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); 150 $package = curl_exec($ch); 151 //獲取curl連接句柄的信息 152 $httpInfo = curl_getinfo($ch); 153 curl_close($ch); 154 155 $info = array_merge(array($package), array($httpInfo)); 156 157 return $info; 158 159 } 160 161 /** 162 * 將資源寫入文件 163 * @param string 資源uri 164 * @param source 資源 165 * @return boolean 166 */ 167 private function saveFile($path, $fileContent) { 168 $fp = fopen($path, 'w'); 169 if (false !== $localFile) { 170 if (false !== fwrite($fp, $fileContent)) { 171 fclose($fp); 172 return true; 173 } 174 } 175 return false; 176 } 177 178 } 179 180 181 182 183 184 185 186 187 ?>

1 <html> 2 <head> 3 4 </head> 5 <body> 6 <button id="uploadImage">點擊上傳圖片</button> 7 <script> 8 $(function(){ 9 $.util.wxMenuImage('{$wxJsApi|default:""}') 10 }); 11 </script> 12 </body> 13 </html>

1 if(typeof($util)=='undefined')$util={}; 2 3 $.util.wxMenuImage = function(json) { 4 if (json.length == 0) return; 5 //解析json變成js對象 6 wxJsApi = JSON.parse(json); 7 8 //通過config接口注入權限驗證配置 9 wx.config({ 10 debug: false, //開啟調試模式,調用的所有api的返回值會在客戶端alert出來 11 appId: wxJsApi.signPackage.appId, //公眾號的唯一標識 12 timestamp: wxJsApi.signPackage.timestamp, //生成簽名的時間戳 13 nonceStr: wxJsApi.signPackage.nonceStr, //生成簽名的隨機串 14 signature: wxJsApi.signPackage.signature, //簽名 15 jsApiList: ['chooseImage', 'uploadImage'] //需要使用的JS接口列表 這里我用了選擇圖片和上傳圖片接口 16 }); 17 18 //通過ready接口處理成功驗證,config信息驗證后會執行ready方法,所有接口調用都必須在config接口獲得結果之后 19 wx.ready(function(){ 20 //得到上傳圖片按鈕 21 document.querySelector('#uploadImage').onclick = function() { 22 var images = {localId:[],serverId:[]}; 23 //調用 拍照或從手機相冊中選圖接口 24 wx.chooseImage({ 25 success: function(res) { 26 if (res.localIds.length != 1) { 27 alert('只能上傳一張圖片'); 28 return; 29 } 30 //返回選定照片的本地ID列表 31 iamges.localId = res.localIds; 32 images.serverId = []; 33 //上傳圖片函數 34 function upload() { 35 //調用上傳圖片接口 36 wx.uploadImage({ 37 localId: images.localId[0], // 需要上傳的圖片的本地ID,由chooseImage接口獲得 38 isShowProcess: 1, // 默認為1,顯示進度提示 39 success: function(res) { 40 //返回圖片的服務器端ID res.serverId,然后調用wxImgCallback函數進行下載圖片操作 41 wxImgCallback(res.serverId); 42 }, 43 fail: function(res) { 44 alert('上傳失敗'); 45 } 46 }); 47 } 48 upload(); 49 } 50 }); 51 } 52 }); 53 } 54 55 56 function wxImgCallback(serverId) { 57 //將serverId傳給wx_upload.php的upload方法 58 var url = 'wx_upload/upload/'+serverId; 59 $.getJSON(url, function(data){ 60 if (data.code == 0) { 61 alert(data.msg); 62 } else if (data.code == 1) { 63 //存儲到服務器成功后的處理 64 // 65 } 66 }); 67 }
代碼中有些方法沒有貼出來,大家要是想看看,可以到http://www.cnblogs.com/it-cen/p/4535219.html 這篇博文去看。
本次講解就到此,這篇博文是給對微信接口開發有興趣的朋友參考,如果你是高手,完全可以繞道。
如果此博文中有哪里講得讓人難以理解,歡迎留言交流,若有講解錯的地方歡迎指出。
如果您覺得您能在此博文學到了新知識,請為我頂一個,如文章中有解釋錯的地方,歡迎指出。
互相學習,共同進步!