JS-SDK之图像接⼝开发详解
由于现在⼿头的项⽬中有⼀个上传证件照认证的功能(⼿机端),之前的思路是直接点击上传,然后直接将图⽚上传到服务器去,这篇⽂章有讲到(),但在⾥打开⽹
页去上传,速度并不快,⽽且,假如我上传⼀张2M⼤的图⽚,也没有对其进⾏压缩处理,这样很影响上传和下载的速度。
所以,我这⾥借助JSSDK的图像接⼝对其进⾏开发实现图⽚上传的功能,为何我选择此接⼝?第⼀,⽬前的项⽬是在中打开的⽹页,利⽤此接⼝,性能肯定是好⼀
点的啦,毕竟是⾃⼰的东西;第⼆,⽤此接⼝,开发效率更⾼嘛;第三,最重要的⼀点,就是它能对图⽚进⾏压缩,假如⼀张2M的图⽚,通过图⽚上传接⼝可以将图⽚
压缩成⼏百K的⼤⼩,这对⽹站的性能是很有帮助的。
⼀、我的思路是:
先调⽤“拍照或从⼿机相册选择图⽚接⼝”—>选择成功图⽚后—>调⽤“上传图⽚接⼝”—>上传成功后(也就是图⽚上传到了服务器上)—>调⽤“下载图⽚接⼝”—>将图⽚下载到⾃
⼰的服务器存储。
⼆、JSSDK的使⽤步骤字符串长度js
1、概述
JS-SDK是公众平台⾯向⽹页开发者提供的基于内的⽹页开发⼯具包。
通过使⽤JS-SDK,⽹页开发者可借助⾼效地使⽤拍照、选图、语⾳、位置等⼿机系统的能⼒,同时可以直接使⽤分享、扫⼀扫、卡券、⽀付等特有的能
⼒,为⽤户提供更优质的⽹页体验。
2、使⽤步骤
步骤⼀:绑定域名
先登录进⼊“设置”的“功能设置”⾥填写“JS接⼝安全域名”。
备注:登录后可在“开发者中⼼”查看对应的接⼝权限。
步骤⼆:引⼊JS⽂件
在需要调⽤JS接⼝的页⾯引⼊如下JS⽂件,(⽀持https):
步骤三:通过config接⼝注⼊权限验证配置
所有需要使⽤JS-SDK的页⾯必须先注⼊配置信息,否则将⽆法调⽤(同⼀个url仅需调⽤⼀次,对于变化url的SPA的web app可在每次url变化时进⾏调⽤)
fig({
2 debug: true, // 开启调试模式,调⽤的所有api的返回值会在客户端alert出来,若要查看传⼊的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
3 appId: '', // 必填,的唯⼀标识
4 timestamp: , // 必填,⽣成签名的时间戳
5 nonceStr: '', // 必填,⽣成签名的随机串
6 signature: '',// 必填,签名,见附录1
7 jsApiList: [] // 必填,需要使⽤的JS接⼝列表,所有JS接⼝列表见附录2
8 });
步骤四:通过ready接⼝处理成功验证
ady(function(){
2
3// config信息验证后会执⾏ready⽅法,所有接⼝调⽤都必须在config接⼝获得结果之后,config是⼀个客户端的异步操作,所以如果需要在页⾯加载时就调⽤相关接⼝,则须把相关接⼝放在ready函数中调⽤来确保正确执⾏。对于⽤户触发时才调4 });
步骤五:通过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
2class wx_upload extends xx_Controller {
3public function __construct() {
4 parent::__construct();
5 }
6
7public function wxUploadImg() {
8//在模板⾥引⼊jssdk的js⽂件
9$this->addResLink('res.wx.qq/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
2class WxModel extends ModelBase{
3public$appId;
4public$appSecret;
5public$token;
6
7public 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*/
20public function signPackage() {
21$protocol = (!empty($_SERVER['HTTPS'] && $_SERVER['HTTPS'] == 'off' || $_SERVER['port'] == 443)) ? '' : '';
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 );
44return$signPackage;
45 }
46
47/**
48 * 创建签名的随机字符串
49 * @param int $length 字符串长度
50 * @return string 随机字符串
51*/
52private function createNonceStr($length == 16) {
53$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
54$str = '';
55for ($i=0; $i < $length; $i++) {
56$str .= substr(mt_rand(0, strlen($chars)), 1);
57 }
58return$str;
59 }
60
61/**
62 * 获取⽤于调⽤JS接⼝的临时票据
63 * @return string
64*/
65private function getJsApiTicket() {
66//先查看redis⾥是否存了jsapi_ticket此值,假如有,就直接返回
67$jsApiTicket = $this->library->redisCache->get('weixin:ticket');
68if (!$jsApiTicket) {
69//先获取access_token(的全局唯⼀票据)
70$accessToken = $this->getApiToken();
71//通过access_token 采⽤http GET⽅式请求获得jsapi_ticket
72$result = $this->callApi("api.weixin.qq/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 }
79return$jsApiTicket;
80 }
81
82/**
83 * 获取众号的全局唯⼀票据access_token
84 * @param boolean $forceRefresh 是否强制刷新
85 * @return string 返回access_token
86*/
87private function getApiToken($forceRefresh = false) {
88//先查看redis是否存了accessToken,如果有了,就不⽤再去server去请求了(提⾼效率)
89$accessToken = $this->library->redisCache->get('weixin:accessToken');
90//强制刷新accessToken或者accessToken为空时就去请求accessToken
91if ($forceRefresh || empty($accessToken)) {
92//请求得到accessToken
93$result = $this->callApi("api.weixin.qq/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 }
99return$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。
1、获取access_token(有效期7200秒,开发者必须在⾃⼰的服务全局缓存access_token)
2、⽤第⼀步拿到的access_token 采⽤http GET⽅式请求获得jsapi_ticket(有效期7200秒,开发者必须在⾃⼰的服务全局缓存jsapi_ticket)
成功返回如下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=value
1&key2=value2…)拼接成字符串string1。这⾥需要注意的是所有参数名
均为⼩写字符。对string1作sha1加密,字段名和字段值都采⽤原始值,不进⾏URL 转义。
即signature=sha1(string1)。⽰例:
noncestr=Wm3WZYTPz0wzccnW
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg
timestamp=1414587457
url=
步骤1. 对所有待签名参数按照字段名的ASCII 码从⼩到⼤排序(字典序)后,使⽤URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1:
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW×tamp=1414587457&url=mp.we
ixin.qq?params=valu 步骤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
1if(typeof($util)=='undefined')$util={};
2
3 $.util.wxMenuImage = function(json) {
4if (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: Str, //⽣成签名的随机串
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() {
22var images = {localId:[],serverId:[]};
23//调⽤拍照或从⼿机相册中选图接⼝
24 wx.chooseImage({
25 success: function(res) {
26if (res.localIds.length != 1) {
27 alert('只能上传⼀张图⽚');
28return;
29 }
30//返回选定照⽚的本地ID列表
31 iamges.localId = res.localIds;
32 images.serverId = [];
33//上传图⽚函数
34function 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
56function wxImgCallback(serverId) {
57//将serverId传给wx_upload.php的upload⽅法
58var url = 'wx_upload/upload/'+serverId;
59 $.getJSON(url, function(data){
60if (de == 0) {
61 alert(data.msg);
62 } else if (de == 1) {
63//存储到服务器成功后的处理
64//
65 }
66 });
67 }
图⽚选择和图⽚上传接⼝调⽤
3、图⽚上传完成后会返回⼀个serverId,然后通过这个来下载图⽚到本地服务器
这⾥先补充下如何调⽤下载图⽚接⼝(我直接复制官⽅⽂档的说明了)
可调⽤本接⼝来获取多媒体⽂件。请注意,视频⽂件不⽀持下载,调⽤该接⼝需http协议。
接⼝调⽤请求说明
http请求⽅式: GET
参数说明
参数是否必须说明
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 ""
错误情况下的返回JSON数据包⽰例如下(⽰例为⽆效媒体ID错误)::
{"errcode":40007,"errmsg":"invalid media_id"}
接下来看⾃⼰写的代码
wx_upload.php
1/*********************图⽚下载到本地服务器****************************************/
2//从服务器读取图⽚,然后下载到本地服务器
3public 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");
8if ($path != false) {
9//将图⽚的路径插⼊数据库去存储
10if ($this->model->weixin->updateByWxid($this->wxId, array('img_path'=>$path))) {
11$this->output->_display(json_encode(
12array(
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//从服务器端下载图⽚到本地服务器
2public function wxDownImg($media_id, $path) {
3//调⽤多媒体⽂件下载接⼝
4$url = "api.weixin.qq/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的值是否是这四种图⽚类型
10if (isset($types[$info['header']['content_type']])) {
11//⽂件的uri
12$path = $path.$types[$info['header']['content_type']];
13 } else {
14return false;
15 }
16
17//将资源写⼊⽂件⾥
18if ($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']]]; 21if (!is_dir($imgPath)) {
22if(mkdir($imgPath)) {
23if (false !== rename($path, $imgPath) {
24return$imgPath;
25 }
26 }
27 }
28return$path;
29 }
30
31return false;
32
33 }
34
35/**
36 * curl请求资源
37 * @param string $url 请求url
38 * @return array
39*/
40private function curl_request($url = '') {
41if ($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
56return$info;
57
58 }
59
60/**
61 * 将资源写⼊⽂件
62 * @param string 资源uri
63 * @param source 资源
64 * @return boolean
65*/
66private function saveFile($path, $fileContent) {
67$fp = fopen($path, 'w');
68if (false !== $localFile) {
69if (false !== fwrite($fp, $fileContent)) {
70fclose($fp);
71return true;
72 }
73 }
74return false;
75 }
从服务器下载图⽚到本地存储接⼝
到这⾥,已经完成了:
先调⽤“拍照或从⼿机相册选择图⽚接⼝”—>选择成功图⽚后—>调⽤“上传图⽚接⼝”—>上传成功后(也就是图⽚上传到了服务器上)—>调⽤“下载图⽚接⼝”—>将图⽚下载到⾃⼰的服务器存储。
这⼀思路的实现。我们⽤到了的选择图⽚接⼝、上传图⽚接⼝和下载媒体资源接⼝。
下⾯我附上这⼀接⼝开发的全部代码:
1 <?php
2class wx_upload extends xx_Controller {
3public function __construct() {
4 parent::__construct();
5 }
6
7public function wxUploadImg() {
8//在模板⾥引⼊jssdk的js⽂件
9$this->addResLink('res.wx.qq/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//从服务器读取图⽚,然后下载到本地服务器
16public 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");
21if ($path != false) {
22//将图⽚的路径插⼊数据库去存储
23if ($this->model->weixin->updateByWxid($this->wxId, array('img_path'=>$path))) {
24$this->output->_display(json_encode(
25array(
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
2class WxModel extends ModelBase{
3public$appId;
4public$appSecret;
5public$token;
6
7public 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*/
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论