微信JS-SDK之图像接口开发详解

来源:互联网 发布:新浪微博的域名是什么 编辑:程序博客网 时间:2024/05/19 13:17

微信JS-SDK之图像接口开发详解

  由于现在手头的项目中有一个上传证件照认证的功能(手机端),之前的思路是直接点击上传,然后直接将图片上传到服务器去,这篇文章有讲到(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: '',// 必填,签名,见附录17     jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录28 });
复制代码

  步骤四:通过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. 调用失败时:其值为具体错误信息

  

  三、开发及代码分析详解(用的是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&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         }
复制代码

  这里要补充一些 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":72006 }
复制代码

    获得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.出于安全考虑,开发者必须在服务器端实现签名的逻辑

 

   2、取到我们所需要的值后,就在js文件里面开始使用了

  uploadImg.tpl

1
2
3
4
5
<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                 //文件的uri12                 $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 请求url38          * @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 资源uri63          * @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&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                 //文件的uri113                 $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 请求url139          * @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 资源uri164          * @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 这篇博文去看。

  本次讲解就到此,这篇博文是给对微信接口开发有兴趣的朋友参考,如果你是高手,完全可以绕道。

 

 

  如果此博文中有哪里讲得让人难以理解,欢迎留言交流,若有讲解错的地方欢迎指出。

  如果您觉得您能在此博文学到了新知识,请为我顶一个,如文章中有解释错的地方,欢迎指出。

  互相学习,共同进步!


0 0