微信JS-SDK之圖像接口開發詳解

  因爲如今手頭的項目中有一個上傳證件照認證的功能(手機端),以前的思路是直接點擊上傳,而後直接將圖片上傳到服務器去,這篇文章有講到(http://www.cnblogs.com/it-cen/p/4535219.html),但在微信裏打開網頁去上傳,速度並不快,並且,假如我上傳一張2M大的圖片,也沒有對其進行壓縮處理,這樣很影響上傳和下載的速度。php

  因此,我這裏藉助微信JSSDK的圖像接口對其進行開發實現圖片上傳的功能,爲什麼我選擇此接口?第一,目前的項目是在微信中打開的網頁,利用此接口,性能確定是好一點的啦,畢竟是微信本身的東西;第二,用此接口,開發效率更高嘛;第三,最重要的一點,就是它能對圖片進行壓縮,假如一張2M的圖片,經過微信圖片上傳接口能夠將圖片壓縮成幾百K的大小,這對網站的性能是頗有幫助的。html

  1、個人思路是:web

先調用「拍照或從手機相冊選擇圖片接口」—>選擇成功圖片後—>調用「上傳圖片接口」—>上傳成功後(也就是圖片上傳到了微信服務器上)—>調用「下載圖片接口」—>將圖片下載到本身的服務器存儲。ajax

 

  2、JSSDK的使用步驟redis

  一、概述 算法

  微信JS-SDK是微信公衆平臺面向網頁開發者提供的基於微信內的網頁開發工具包。數據庫

  經過使用微信JS-SDK,網頁開發者可藉助微信高效地使用拍照、選圖、語音、位置等手機系統的能力,同時能夠直接使用微信分享、掃一掃、卡券、支付等微信特有的能力,爲微信用戶提供更優質的網頁體驗。json

  二、使用步驟api

  步驟一:綁定域名緩存

  先登陸微信公衆平臺進入「公衆號設置」的「功能設置」裏填寫「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對象)來調用,參數是一個對象,除了每一個接口自己須要傳的參數以外,還有如下通用參數:

  1. success:接口調用成功時執行的回調函數。
  2. fail:接口調用失敗時執行的回調函數。
  3. complete:接口調用完成時執行的回調函數,不管成功或失敗都會執行。
  4. cancel:用戶點擊取消時的回調函數,僅部分有用戶取消操做的api纔會用到。
  5. trigger: 監聽Menu中的按鈕點擊時觸發的方法,該方法僅支持Menu中的相關接口。

  備註:不要嘗試在trigger中使用ajax異步請求修改本次分享的內容,由於客戶端分享操做是一個同步操做,這時候使用ajax的回包會尚未返回


  以上幾個函數都帶有一個參數,類型爲對象,其中除了每一個接口自己返回的數據以外,還有一個通用屬性errMsg,其值格式以下:

    1. 調用成功時:"xxx:ok" ,其中xxx爲調用的接口名
    2. 用戶取消時:"xxx:cancel",其中xxx爲調用的接口名
    3. 調用失敗時:其值爲具體錯誤信息

  

  3、開發及代碼分析詳解(用的是CI框架,只要是MVC模式均可以)

  一、先在服務器端取到:公衆號的惟一標識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&timestamp=$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         }
獲取到appId、nonceStr、timestamp、signature模型

  這裏要補充一些 JS-SDK使用權限簽名算法 的思路和注意點(這裏我直接複製官網文檔給你們看看)

    jsapi_ticket

    生成簽名以前必須先了解一下jsapi_ticket,jsapi_ticket是公衆號用於調用微信JS接口的臨時票據。正常狀況下,jsapi_ticket的有效期爲7200秒,經過access_token來獲取。因爲獲取jsapi_ticket的api調用次數很是有限,頻繁刷新jsapi_ticket會致使api調用受限,影響自身業務,開發者必須在本身的服務全局緩存jsapi_ticket 。

    一、獲取access_token(有效期7200秒,開發者必須在本身的服務全局緩存access_token

    二、用第一步拿到的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&timestamp=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.出於安全考慮,開發者必須在服務器端實現簽名的邏輯

 

   二、取到咱們所須要的值後,就在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 }
圖片選擇和圖片上傳接口調用

   

  三、圖片上傳完成後會返回一個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 ?>
wx_upload.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&timestamp=$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 ?>
WeixinModel.php
 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>
uploadImg.tpl
 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 }
uploadImg.js

  代碼中有些方法沒有貼出來,你們要是想看看,能夠到http://www.cnblogs.com/it-cen/p/4535219.html 這篇博文去看。

  本次講解就到此,這篇博文是給對微信接口開發有興趣的朋友參考,若是你是高手,徹底能夠繞道。

 

 

  若是此博文中有哪裏講得讓人難以理解,歡迎留言交流,如有講解錯的地方歡迎指出。

  若是您以爲您能在此博文學到了新知識,請爲我頂一個,如文章中有解釋錯的地方,歡迎指出。

  互相學習,共同進步!

相關文章
相關標籤/搜索