I use the image interface of WeChat JSSDK to develop it to implement the image upload function. Why did I choose this interface? First, the current project is a web page opened in WeChat. Using this interface, the performance will definitely be better. After all, it is WeChat’s own thing; second, using this interface, the development efficiency will be higher; third, finally The important point is that it can compress images. For example, if a 2M image is used, the image can be compressed into a size of several hundred K through the WeChat image upload interface, which is very helpful for the performance of the website.
1. My idea is:
First call the "photograph or select a picture from the mobile phone album interface" -> After selecting a successful picture -> call the 'upload picture interface' -> After the upload is successful (that is, the image is uploaded to the WeChat server) -> Call the "Download Image Interface" -> Download the image to your own server for storage.
2. Steps for using JSSDK
1. Overview
WeChat JS-SDK is a WeChat-based WeChat public platform for web developers. web development toolkit.
By using WeChat JS-SDK, web developers can use WeChat to efficiently use the capabilities of mobile phone systems such as taking pictures, selecting pictures, voice, and location. At the same time, they can directly use WeChat to share, scan, and coupons. WeChat’s unique capabilities such as payment provide WeChat users with a better web experience.
2. Steps to use
Step 1: Bind domain name
First log in to the WeChat public platform and enter the "Function Settings" of "Official Account Settings" to fill in "JS Interface Security" domain name".
Note: After logging in, you can view the corresponding interface permissions in the "Developer Center".
Step 2: Import JS files
Import the following JS files on the page that needs to call the JS interface (https is supported): http://res.wx.qq.com/open/js /jweixin-1.0.0.js
Step 3: Inject permission verification configuration through the config interface
All pages that need to use JS-SDK must first inject configuration information, otherwise it will not be called (same as A URL only needs to be called once, and the SPA web app that changes the URL can be called every time the URL changes)
wx.config({
????debug:?true,?//?開(kāi)啟調(diào)試模式,調(diào)用的所有api的返回值會(huì)在客戶(hù)端alert出來(lái),若要查看傳入的參數(shù),可以在pc端打開(kāi),參數(shù)信息會(huì)通過(guò)log打出,僅在pc端時(shí)才會(huì)打印。
????appId:?'',?//?必填,公眾號(hào)的唯一標(biāo)識(shí)
????timestamp:?,?//?必填,生成簽名的時(shí)間戳
????nonceStr:?'',?//?必填,生成簽名的隨機(jī)串
????signature:?'',//?必填,簽名,見(jiàn)附錄1
????jsApiList:?[]?//?必填,需要使用的JS接口列表,所有JS接口列表見(jiàn)附錄2
});
Step 4: Process successful verification through the ready interface
wx.ready(function(){
????//?config信息驗(yàn)證后會(huì)執(zhí)行ready方法,所有接口調(diào)用都必須在config接口獲得結(jié)果之后,config是一個(gè)客戶(hù)端的異步操作,所以如果需要在頁(yè)面加載時(shí)就調(diào)用相關(guān)接口,則須把相關(guān)接口放在ready函數(shù)中調(diào)用來(lái)確保正確執(zhí)行。對(duì)于用戶(hù)觸發(fā)時(shí)才調(diào)用的接口,則可以直接調(diào)用,不需要放在ready函數(shù)中。
});
Step 5 : Handling failed verification through error interface
wx.error(function(res){
????//?config信息驗(yàn)證失敗會(huì)執(zhí)行error函數(shù),如簽名過(guò)期導(dǎo)致驗(yàn)證失敗,具體錯(cuò)誤信息可以打開(kāi)config的debug模式查看,也可以在返回的res參數(shù)中查看,對(duì)于SPA可以在這里更新簽名。
});
Interface calling instructions
All interfaces are called through wx objects (jWeixin objects can also be used). The parameter is an object, except that each interface itself needs to be passed In addition to the parameters, there are the following general parameters:
success: the callback function executed when the interface call is successful.
fail: The callback function executed when the interface call fails.
complete: The callback function executed when the interface call is completed, regardless of success or failure.
cancel: The callback function when the user clicks cancel. It is only used by some APIs where the user cancels the operation.
trigger: A method that is triggered when a button in the Menu is clicked. This method only supports related interfaces in the Menu.
Note: Do not try to use ajax asynchronous request in the trigger to modify the content of this share, because the client sharing operation is a synchronous operation, and the return packet using ajax will not be available at this time. return.
The above functions all have one parameter, which is of type object. In addition to the data returned by each interface itself, there is also a general attribute errMsg, whose value format is as follows:
When the call is successful: "xxx:ok", where xxx is the interface name of the call
When the user cancels: "xxx:cancel" , where xxx is the name of the called interface
When the call fails: its value is the specific error message
3. Detailed explanation of development and code analysis (CI framework is used, as long as it is MVC mode)
1. First obtain on the server side: the unique identifier appId of the public account, and the timestamp of the generated signature , generate a signed random string nonceStr and signature signature.
<?php
class wx_upload extends xx_Controller {
public function __construct() {
parent::__construct();
}
public function wxUploadImg() {
//在模板里引入jssdk的js文件
$this->addResLink('http://res.wx.qq.com/open/js/jweixin-1.0.0.js');
????????//取得:公眾號(hào)的唯一標(biāo)識(shí)appId、生成簽名的時(shí)間戳timestamp、生成簽名的隨機(jī)串nonceStr、簽名signature這些值,并以json形式傳到模板頁(yè)面
????????$this->smartyData['wxJsApi']?=?json_encode(array('signPackage'?=>?$this->model->weixin->signPackage()));
????}
Image upload controller
<?php
class WxModel extends ModelBase{
public $appId;
public $appSecret;
public $token;
public function __construct() {
parent::__construct();
//審核通過(guò)的移動(dòng)應(yīng)用所給的AppID和AppSecret
$this->appId?=?'wx0000000000000000';
????????????$this->appSecret?=?'00000000000000000000000000000';
????????????$this->token?=?'00000000';
????????}
????????/**
?????????*?獲取jssdk所需參數(shù)的所有值
?????????*?@return?array
?????????*/
????????public?function?signPackage()?{
????????????$protocol?=?(!empty($_SERVER['HTTPS']?&&?$_SERVER['HTTPS']?==?'off'?||?$_SERVER['port']?==?443))???'https://'?:?'http://';
????????????//當(dāng)前網(wǎng)頁(yè)的URL
????????????$url?=?"$protocol$_SERVER['host']$_SERVER['REQUEST_URI']";
????????????//生成簽名的時(shí)間戳
????????????$timestamp?=?time();
????????????//生成簽名的隨機(jī)串
????????????$nonceStr?=?$this->createNonceStr();
????????????//獲取公眾號(hào)用于調(diào)用微信JS接口的臨時(shí)票據(jù)
????????????$jsApiTicket?=?$this->getJsApiTicket();
????????????//對(duì)所有待簽名參數(shù)按照字段名的ASCII?碼從小到大排序(字典序)后,
????????????//使用URL鍵值對(duì)的格式(即key1=value1&key2=value2…)拼接成字符串$str。
????????????//這里需要注意的是所有參數(shù)名均為小寫(xiě)字符
????????????$str?=?"jsapi_ticket=$jsApiTicket&noncestr=$nonceStr×tamp=$timestamp&url=$url";
????????????//對(duì)$str進(jìn)行sha1簽名,得到signature:
????????????$signature?=?sha1($str);
????????????$signPackage?=?array(
????????????????"appId"?????=>?$this->AppId,
????????????????"nonceStr"??=>?$nonceStr,
????????????????"timestamp"?=>?$timestamp,
????????????????"url"???????=>?$url,
????????????????"signature"?=>?$signature,
????????????????"rawString"?=>?$string
????????????????);
????????????return?$signPackage;
????????}
????????/**
?????????*?創(chuàng)建簽名的隨機(jī)字符串
?????????*?@param??int?$length?字符串長(zhǎng)度
?????????*?@return?string??????隨機(jī)字符串
?????????*/
????????private?function?createNonceStr($length?==?16)?{
????????????$chars?=?'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
????????????$str?=?'';
????????????for?($i=0;?$i?< $length; $i++) {
$str .= substr(mt_rand(0, strlen($chars)), 1);
}
return $str;
}
/**
* 獲取公眾號(hào)用于調(diào)用微信JS接口的臨時(shí)票據(jù)
* @return string
*/
private function getJsApiTicket() {
//先查看redis里是否存了jsapi_ticket此值,假如有,就直接返回
$jsApiTicket = $this->library->redisCache->get('weixin:ticket');
????????????if?(!$jsApiTicket)?{
????????????????//先獲取access_token(公眾號(hào)的全局唯一票據(jù))
????????????????$accessToken?=?$this->getApiToken();
????????????????//通過(guò)access_token?采用http?GET方式請(qǐng)求獲得jsapi_ticket
????????????????$result?=?$this->callApi("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=$accessToken&type=jsapi");
????????????????//得到了jsapi_ticket
????????????????$jsApiTicket?=?$result['ticket'];
????????????????//將jsapi_ticket緩存到redis里面,下次就不用再請(qǐng)求去取了
????????????????$expire?=?max(1,?intval($result['expire'])?-?60);
????????????????$this->library->redisCache->set('weixin:ticket',?$jsApiTicket,?$expire);
????????????}
????????????return?$jsApiTicket;
????????}
????????/**
?????????*?獲取眾號(hào)的全局唯一票據(jù)access_token
?????????*?@param??boolean?$forceRefresh?是否強(qiáng)制刷新
?????????*?@return?string????????????????返回access_token
?????????*/
????????private?function?getApiToken($forceRefresh?=?false)?{
????????????//先查看redis是否存了accessToken,如果有了,就不用再去微信server去請(qǐng)求了(提高效率)
????????????$accessToken?=?$this->library->redisCache->get('weixin:accessToken');
????????????//強(qiáng)制刷新accessToken或者accessToken為空時(shí)就去請(qǐng)求accessToken
????????????if?($forceRefresh?||?empty($accessToken))?{
????????????????//請(qǐng)求得到accessToken
????????????????$result?=?$this->callApi("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appId}&secret={$this->appSecret}");
????????????????$accessToken?=?$result['access_token'];
????????????????$expire?=?max(1,?intval($result['expire'])?-?60);
????????????????//將其存進(jìn)redis里面去
????????????????$this->library->redisCache->set('weixin:accessToken',?$accessToken,?$expire);
????????????}
????????????return?$accessToken;
????????}
Get appId, nonceStr, timestamp, signature model
Here are some ideas and points to pay attention to when using the permission signature algorithm of JS-SDK (Here I directly copy the official website document for everyone to see)
jsapi_ticket
Before generating a signature, you must first understand jsapi_ticket. jsapi_ticket is a temporary ticket used by public accounts to call the WeChat JS interface. Under normal circumstances, the validity period of jsapi_ticket is 7200 seconds and is obtained through access_token. Since the number of api calls to obtain jsapi_ticket is very limited, frequent refresh of jsapi_ticket will result in limited api calls and affect their own business. Developers must cache jsapi_ticket globally in their own services.
1. Obtain access_token (valid for 7200 seconds, developers must cache access_token globally in their own services)
2. Use the access_token obtained in the first step to request jsapi_ticket using http GET method ( Validity period is 7200 seconds, developers must cache jsapi_ticket globally in their own services)
https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi
成功返回如下JSON:
{
"errcode":0,
"errmsg":"ok",
"ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
"expires_in":7200
}
獲得jsapi_ticket之后,就可以生成JS-SDK權(quán)限驗(yàn)證的簽名了。
簽名算法
簽名生成規(guī)則如下:參與簽名的字段包括noncestr(隨機(jī)字符串), 有效的jsapi_ticket, timestamp(時(shí)間戳), url(當(dāng)前網(wǎng)頁(yè)的URL,不包含#及其后面部分) 。對(duì)所有待簽名參數(shù)按照字段名的ASCII 碼從小到大排序(字典序)后,使用URL鍵值對(duì)的格式(即key1=value1&key2=value2…)拼接成字符串string1。這里需要注意的是所有參數(shù)名均為小寫(xiě)字符。對(duì)string1作sha1加密,字段名和字段值都采用原始值,不進(jìn)行URL 轉(zhuǎn)義。
即signature=sha1(string1)。 示例:
noncestr=Wm3WZYTPz0wzccnW
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg
timestamp=1414587457
url=http://mp.weixin.qq.com?params=value
步驟1. 對(duì)所有待簽名參數(shù)按照字段名的ASCII 碼從小到大排序(字典序)后,使用URL鍵值對(duì)的格式(即key1=value1&key2=value2…)拼接成字符串string1:
jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW×tamp=1414587457&url=http://www.miracleart.cn/
步驟2. 對(duì)string1進(jìn)行sha1簽名,得到signature:
0f9de62fce790f9a083d5c99e95740ceb90c27ed
注意事項(xiàng)
1.簽名用的noncestr和timestamp必須與wx.config中的nonceStr和timestamp相同。
2.簽名用的url必須是調(diào)用JS接口頁(yè)面的完整URL。
3.出于安全考慮,開(kāi)發(fā)者必須在服務(wù)器端實(shí)現(xiàn)簽名的邏輯。
?
? 2、取到我們所需要的值后,就在js文件里面開(kāi)始使用了
uploadImg.tpl
<script>
????????$(function(){
????????????$.util.wxMenuImage('{$wxJsApi|default:""}')
????????});
</script>
? uploadImg.js
if(typeof($util)=='undefined')$util={};
$.util.wxMenuImage?=?function(json)?{
????if?(json.length?==?0)?return;?
????//解析json變成js對(duì)象
????wxJsApi?=?JSON.parse(json);
????//通過(guò)config接口注入權(quán)限驗(yàn)證配置
????wx.config({
????????debug:?false,???//開(kāi)啟調(diào)試模式,調(diào)用的所有api的返回值會(huì)在客戶(hù)端alert出來(lái)
????????appId:?wxJsApi.signPackage.appId,???//公眾號(hào)的唯一標(biāo)識(shí)
????????timestamp:?wxJsApi.signPackage.timestamp,???//生成簽名的時(shí)間戳
????????nonceStr:?wxJsApi.signPackage.nonceStr,?//生成簽名的隨機(jī)串
????????signature:?wxJsApi.signPackage.signature,???//簽名
????????jsApiList:?['chooseImage',?'uploadImage']???//需要使用的JS接口列表?這里我用了選擇圖片和上傳圖片接口
????});
????//通過(guò)ready接口處理成功驗(yàn)證,config信息驗(yàn)證后會(huì)執(zhí)行ready方法,所有接口調(diào)用都必須在config接口獲得結(jié)果之后
????wx.ready(function(){
????????//得到上傳圖片按鈕
????????document.querySelector('#uploadImage').onclick?=?function()?{
????????????var?images?=?{localId:[],serverId:[]};
????????????//調(diào)用?拍照或從手機(jī)相冊(cè)中選圖接口
????????????wx.chooseImage({
????????????????success:?function(res)?{
????????????????????if?(res.localIds.length?!=?1)?{
????????????????????????alert('只能上傳一張圖片');
????????????????????????return;
????????????????????}
????????????????????//返回選定照片的本地ID列表
????????????????????iamges.localId?=?res.localIds;
????????????????????images.serverId?=?[];
????????????????????//上傳圖片函數(shù)
????????????????????function?upload()?{
????????????????????????//調(diào)用上傳圖片接口
????????????????????????wx.uploadImage({
????????????????????????????localId:?images.localId[0],?//?需要上傳的圖片的本地ID,由chooseImage接口獲得
????????????????????????????isShowProcess:?1,???//?默認(rèn)為1,顯示進(jìn)度提示
????????????????????????????success:?function(res)?{
????????????????????????????????//返回圖片的服務(wù)器端ID?res.serverId,然后調(diào)用wxImgCallback函數(shù)進(jìn)行下載圖片操作
????????????????????????????????wxImgCallback(res.serverId);
????????????????????????????},
????????????????????????????fail:?function(res)?{
????????????????????????????????alert('上傳失敗');
????????????????????????????}
????????????????????????});
????????????????????}
????????????????????upload();
????????????????}
????????????});
????????}
????});
}
function?wxImgCallback(serverId)?{
????//將serverId傳給wx_upload.php的upload方法
????var?url?=?'wx_upload/upload/'+serverId;
????$.getJSON(url,?function(data){
????????if?(data.code?==?0)?{
????????????alert(data.msg);
????????}?else?if?(data.code?==?1)?{
????????????//存儲(chǔ)到服務(wù)器成功后的處理
????????????//
????????}
????});
}
圖片選擇和圖片上傳接口調(diào)用
圖片選擇和圖片上傳接口調(diào)用
?
3、圖片上傳完成后會(huì)返回一個(gè)serverId,然后通過(guò)這個(gè)來(lái)下載圖片到本地服務(wù)器
這里先補(bǔ)充下如何調(diào)用下載圖片接口(我直接復(fù)制官方文檔的說(shuō)明了)
公眾號(hào)可調(diào)用本接口來(lái)獲取多媒體文件。請(qǐng)注意,視頻文件不支持下載,調(diào)用該接口需http協(xié)議。
接口調(diào)用請(qǐng)求說(shuō)明
http請(qǐng)求方式:?GET
http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID
參數(shù)說(shuō)明
參數(shù) |
是否必須 |
說(shuō)明 |
access_token |
是 |
調(diào)用接口憑證 |
media_id |
是 |
媒體文件ID |
返回說(shuō)明
正確情況下的返回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"
錯(cuò)誤情況下的返回JSON數(shù)據(jù)包示例如下(示例為無(wú)效媒體ID錯(cuò)誤)::
{"errcode":40007,"errmsg":"invalid?media_id"}
接下來(lái)看自己寫(xiě)的代碼
wx_upload.php
/*********************圖片下載到本地服務(wù)器****************************************/
????//從微信服務(wù)器讀取圖片,然后下載到本地服務(wù)器
????public?function?upload($media_id)?{
????????//圖片文件名
????????$fileName?=?md5($this->wxId."/$media_id");
????????//調(diào)用下載圖片接口,返回路徑
????????$path?=?$this->weixin->wxDownImg($media_id,?sys_get_temp_dir()."$fileName");
????????if?($path?!=?false)?{
????????????//將圖片的路徑插入數(shù)據(jù)庫(kù)去存儲(chǔ)
????????????if?($this->model->weixin->updateByWxid($this->wxId,?array('img_path'=>$path)))?{
????????????????$this->output->_display(json_encode(
????????????????????array(
????????????????????????????'code'=>1,
????????????????????????????'msg'=>'上傳成功',
????????????????????????????'fileUrl'?=>$path;
????????????????????????)
????????????????));
????????????}?else?{
????????????????$this->output->_display(json_encode2(array('code'=>0,'msg'?=>?'上傳失敗','err'=>'1')));
????????????}
????????}?else?{
????????????$this->output->_display(json_encode2(array('code'=>0,'msg'?=>?'上傳失敗','err'=>'2')));
????????}
????????
????}
從微信服務(wù)器下載圖片到本地存儲(chǔ)
//從微信服務(wù)器端下載圖片到本地服務(wù)器
????????public?function?wxDownImg($media_id,?$path)?{
????????????//調(diào)用?多媒體文件下載接口
????????????$url?=?"https://api.weixin.qq.com/cgi-bin/media/get?access_token={$this->model->weixin->_getApiToken()}&media_id=$media_id";
????????????//用curl請(qǐng)求,返回文件資源和curl句柄的信息
????????????$info?=?$this->curl_request($url);
????????????//文件類(lèi)型
????????????$types?=?array('image/bmp'=>'.bmp',?'image/gif'=>'.gif',?'image/jpeg'=>'.jpg',?'image/png'=>'.png');
????????????//判斷響應(yīng)首部里的的content-type的值是否是這四種圖片類(lèi)型
????????????if?(isset($types[$info['header']['content_type']]))?{
????????????????//文件的uri
????????????????$path?=?$path.$types[$info['header']['content_type']];
????????????}?else?{
????????????????return?false;
????????????}
????????????//將資源寫(xiě)入文件里
????????????if?($this->saveFile($path,?$info['body']))?{
????????????????//將文件保存在本地目錄
????????????????$imgPath?=?rtrim(base_url(),?'/').'/img'.date('Ymd').'/'.md5($this->controller->wxId.$media_id).$types[$info['header'['content_type']]];
????????????????if?(!is_dir($imgPath))?{
????????????????????if(mkdir($imgPath))?{
????????????????????????if?(false?!==?rename($path,?$imgPath)?{
????????????????????????????return?$imgPath;
????????????????????????}
????????????????????}
????????????????}
????????????????return?$path;
????????????}
????????????return?false;
????????}
????????/**
?????????*?curl請(qǐng)求資源
?????????*?@param??string?$url?請(qǐng)求url
?????????*?@return?array?
?????????*/
????????private?function?curl_request($url?=?'')?{
????????????if?($url?==?'')?return;
????????????$ch?=?curl_init();
????????????//這里返回響應(yīng)報(bào)文時(shí),只要body的內(nèi)容,其他的都不要
????????????curl_setopt($ch,?CURLOPT_HEADER,?0);
????????????curl_setopt($ch,?CURLOPT_NOBODY,?0);
????????????curl_setopt($ch,?CURLOPT_SSL_VERIFYPEER,?false);
????????????curl_setopt($ch,?CURLOPT_SSL_VERIFYHOST,?false);
????????????curl_setopt($ch,?CURLOPT_RETURNTRANSFER,?1);
????????????$package?=?curl_exec($ch);
????????????//獲取curl連接句柄的信息
????????????$httpInfo?=?curl_getinfo($ch);
????????????curl_close($ch);
????????????$info?=?array_merge(array($package),?array($httpInfo));
????????????return?$info;
????????}
????????/**
?????????*?將資源寫(xiě)入文件
?????????*?@param??string?資源uri
?????????*?@param??source?資源
?????????*?@return?boolean?
?????????*/
????????private?function?saveFile($path,?$fileContent)?{
????????????$fp?=?fopen($path,?'w');
????????????if?(false?!==?$localFile)?{
????????????????if?(false?!==?fwrite($fp,?$fileContent))?{
????????????????????fclose($fp);
????????????????????return?true;
????????????????}
????????????}
????????????return?false;
????????}
從微信服務(wù)器下載圖片到本地存儲(chǔ)接口
到這里,已經(jīng)完成了:
先調(diào)用“拍照或從手機(jī)相冊(cè)選擇圖片接口”—>選擇成功圖片后—>調(diào)用“上傳圖片接口”—>上傳成功后(也就是圖片上傳到了微信服務(wù)器上)—>調(diào)用“下載圖片接口”—>將圖片下載到自己的服務(wù)器存儲(chǔ)。
這一思路的實(shí)現(xiàn)。我們用到了微信的選擇圖片接口、上傳圖片接口和下載媒體資源接口。
下面我附上這一接口開(kāi)發(fā)的全部代碼:
<?php
class wx_upload extends xx_Controller {
public function __construct() {
parent::__construct();
}
public function wxUploadImg() {
//在模板里引入jssdk的js文件
$this->addResLink('http://res.wx.qq.com/open/js/jweixin-1.0.0.js');
????????//取得:公眾號(hào)的唯一標(biāo)識(shí)appId、生成簽名的時(shí)間戳timestamp、生成簽名的隨機(jī)串nonceStr、簽名signature這些值,并以json形式傳到模板頁(yè)面
????????$this->smartyData['wxJsApi']?=?json_encode(array('signPackage'?=>?$this->model->weixin->signPackage()));
????}
????/*********************圖片下載到本地服務(wù)器****************************************/
????//從微信服務(wù)器讀取圖片,然后下載到本地服務(wù)器
????public?function?upload($media_id)?{
????????//圖片文件名
????????$fileName?=?md5($this->wxId."/$media_id");
????????//調(diào)用下載圖片接口,返回路徑
????????$path?=?$this->weixin->wxDownImg($media_id,?sys_get_temp_dir()."$fileName");
????????if?($path?!=?false)?{
????????????//將圖片的路徑插入數(shù)據(jù)庫(kù)去存儲(chǔ)
????????????if?($this->model->weixin->updateByWxid($this->wxId,?array('img_path'=>$path)))?{
????????????????$this->output->_display(json_encode(
????????????????????array(
????????????????????????????'code'=>1,
????????????????????????????'msg'=>'上傳成功',
????????????????????????????'fileUrl'?=>$path;
????????????????????????)
????????????????));
????????????}?else?{
????????????????$this->output->_display(json_encode2(array('code'=>0,'msg'?=>?'上傳失敗','err'=>'1')));
????????????}
????????}?else?{
????????????$this->output->_display(json_encode2(array('code'=>0,'msg'?=>?'上傳失敗','err'=>'2')));
????????}
????????
????}
}
?>
<?php
class WxModel extends ModelBase{
public $appId;
public $appSecret;
public $token;
public function __construct() {
parent::__construct();
//審核通過(guò)的移動(dòng)應(yīng)用所給的AppID和AppSecret
$this->appId?=?'wx0000000000000000';
????????????$this->appSecret?=?'00000000000000000000000000000';
????????????$this->token?=?'00000000';
????????}
????????/**
?????????*?獲取jssdk所需參數(shù)的所有值
?????????*?@return?array
?????????*/
????????public?function?signPackage()?{
????????????$protocol?=?(!empty($_SERVER['HTTPS']?&&?$_SERVER['HTTPS']?==?'off'?||?$_SERVER['port']?==?443))???'https://'?:?'http://';
????????????//當(dāng)前網(wǎng)頁(yè)的URL
????????????$url?=?"$protocol$_SERVER['host']$_SERVER['REQUEST_URI']";
????????????//生成簽名的時(shí)間戳
????????????$timestamp?=?time();
????????????//生成簽名的隨機(jī)串
????????????$nonceStr?=?$this->createNonceStr();
????????????//獲取公眾號(hào)用于調(diào)用微信JS接口的臨時(shí)票據(jù)
????????????$jsApiTicket?=?$this->getJsApiTicket();
????????????//對(duì)所有待簽名參數(shù)按照字段名的ASCII?碼從小到大排序(字典序)后,
????????????//使用URL鍵值對(duì)的格式(即key1=value1&key2=value2…)拼接成字符串$str。
????????????//這里需要注意的是所有參數(shù)名均為小寫(xiě)字符
????????????$str?=?"jsapi_ticket=$jsApiTicket&noncestr=$nonceStr×tamp=$timestamp&url=$url";
????????????//對(duì)$str進(jìn)行sha1簽名,得到signature:
????????????$signature?=?sha1($str);
????????????$signPackage?=?array(
????????????????"appId"?????=>?$this->AppId,
????????????????"nonceStr"??=>?$nonceStr,
????????????????"timestamp"?=>?$timestamp,
????????????????"url"???????=>?$url,
????????????????"signature"?=>?$signature,
????????????????"rawString"?=>?$string
????????????????);
????????????return?$signPackage;
????????}
????????/**
?????????*?創(chuàng)建簽名的隨機(jī)字符串
?????????*?@param??int?$length?字符串長(zhǎng)度
?????????*?@return?string??????隨機(jī)字符串
?????????*/
????????private?function?createNonceStr($length?==?16)?{
????????????$chars?=?'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
????????????$str?=?'';
????????????for?($i=0;?$i?< $length; $i++) {
$str .= substr(mt_rand(0, strlen($chars)), 1);
}
return $str;
}
/**
* 獲取公眾號(hào)用于調(diào)用微信JS接口的臨時(shí)票據(jù)
* @return string
*/
private function getJsApiTicket() {
//先查看redis里是否存了jsapi_ticket此值,假如有,就直接返回
$jsApiTicket = $this->library->redisCache->get('weixin:ticket');
????????????if?(!$jsApiTicket)?{
????????????????//先獲取access_token(公眾號(hào)的全局唯一票據(jù))
????????????????$accessToken?=?$this->getApiToken();
????????????????//通過(guò)access_token?采用http?GET方式請(qǐng)求獲得jsapi_ticket
????????????????$result?=?$this->callApi("https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=$accessToken&type=jsapi");
????????????????//得到了jsapi_ticket
????????????????$jsApiTicket?=?$result['ticket'];
????????????????//將jsapi_ticket緩存到redis里面,下次就不用再請(qǐng)求去取了
????????????????$expire?=?max(1,?intval($result['expire'])?-?60);
????????????????$this->library->redisCache->set('weixin:ticket',?$jsApiTicket,?$expire);
????????????}
????????????return?$jsApiTicket;
????????}
????????/**
?????????*?獲取眾號(hào)的全局唯一票據(jù)access_token
?????????*?@param??boolean?$forceRefresh?是否強(qiáng)制刷新
?????????*?@return?string????????????????返回access_token
?????????*/
????????private?function?getApiToken($forceRefresh?=?false)?{
????????????//先查看redis是否存了accessToken,如果有了,就不用再去微信server去請(qǐng)求了(提高效率)
????????????$accessToken?=?$this->library->redisCache->get('weixin:accessToken');
????????????//強(qiáng)制刷新accessToken或者accessToken為空時(shí)就去請(qǐng)求accessToken
????????????if?($forceRefresh?||?empty($accessToken))?{
????????????????//請(qǐng)求得到accessToken
????????????????$result?=?$this->callApi("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={$this->appId}&secret={$this->appSecret}");
????????????????$accessToken?=?$result['access_token'];
????????????????$expire?=?max(1,?intval($result['expire'])?-?60);
????????????????//將其存進(jìn)redis里面去
????????????????$this->library->redisCache->set('weixin:accessToken',?$accessToken,?$expire);
????????????}
????????????return?$accessToken;
????????}
????????//從微信服務(wù)器端下載圖片到本地服務(wù)器
????????public?function?wxDownImg($media_id,?$path)?{
????????????//調(diào)用?多媒體文件下載接口
????????????$url?=?"https://api.weixin.qq.com/cgi-bin/media/get?access_token={$this->model->weixin->_getApiToken()}&media_id=$media_id";
????????????//用curl請(qǐng)求,返回文件資源和curl句柄的信息
????????????$info?=?$this->curl_request($url);
????????????//文件類(lèi)型
????????????$types?=?array('image/bmp'=>'.bmp',?'image/gif'=>'.gif',?'image/jpeg'=>'.jpg',?'image/png'=>'.png');
????????????//判斷響應(yīng)首部里的的content-type的值是否是這四種圖片類(lèi)型
????????????if?(isset($types[$info['header']['content_type']]))?{
????????????????//文件的uri
????????????????$path?=?$path.$types[$info['header']['content_type']];
????????????}?else?{
????????????????return?false;
????????????}
????????????//將資源寫(xiě)入文件里
????????????if?($this->saveFile($path,?$info['body']))?{
????????????????//將文件保存在本地目錄
????????????????$imgPath?=?rtrim(base_url(),?'/').'/img'.date('Ymd').'/'.md5($this->controller->wxId.$media_id).$types[$info['header'['content_type']]];
????????????????if?(!is_dir($imgPath))?{
????????????????????if(mkdir($imgPath))?{
????????????????????????if?(false?!==?rename($path,?$imgPath)?{
????????????????????????????return?$imgPath;
????????????????????????}
????????????????????}
????????????????}
????????????????return?$path;
????????????}
????????????return?false;
????????}
????????/**
?????????*?curl請(qǐng)求資源
?????????*?@param??string?$url?請(qǐng)求url
?????????*?@return?array?
?????????*/
????????private?function?curl_request($url?=?'')?{
????????????if?($url?==?'')?return;
????????????$ch?=?curl_init();
????????????//這里返回響應(yīng)報(bào)文時(shí),只要body的內(nèi)容,其他的都不要
????????????curl_setopt($ch,?CURLOPT_HEADER,?0);
????????????curl_setopt($ch,?CURLOPT_NOBODY,?0);
????????????curl_setopt($ch,?CURLOPT_SSL_VERIFYPEER,?false);
????????????curl_setopt($ch,?CURLOPT_SSL_VERIFYHOST,?false);
????????????curl_setopt($ch,?CURLOPT_RETURNTRANSFER,?1);
????????????$package?=?curl_exec($ch);
????????????//獲取curl連接句柄的信息
????????????$httpInfo?=?curl_getinfo($ch);
????????????curl_close($ch);
????????????$info?=?array_merge(array($package),?array($httpInfo));
????????????return?$info;
????????}
????????/**
?????????*?將資源寫(xiě)入文件
?????????*?@param??string?資源uri
?????????*?@param??source?資源
?????????*?@return?boolean?
?????????*/
????????private?function?saveFile($path,?$fileContent)?{
????????????$fp?=?fopen($path,?'w');
????????????if?(false?!==?$localFile)?{
????????????????if?(false?!==?fwrite($fp,?$fileContent))?{
????????????????????fclose($fp);
????????????????????return?true;
????????????????}
????????????}
????????????return?false;
????????}
????}
?>
<html>
????<head>
????????
????</head>
????<body>
????????<button id="uploadImage">點(diǎn)擊上傳圖片</button>
????????<script>
????????$(function(){
????????????$.util.wxMenuImage('{$wxJsApi|default:""}')
????????});
????????</script>
????</body>
</html>
if(typeof($util)=='undefined')$util={};
$.util.wxMenuImage?=?function(json)?{
????if?(json.length?==?0)?return;?
????//解析json變成js對(duì)象
????wxJsApi?=?JSON.parse(json);
????//通過(guò)config接口注入權(quán)限驗(yàn)證配置
????wx.config({
????????debug:?false,???//開(kāi)啟調(diào)試模式,調(diào)用的所有api的返回值會(huì)在客戶(hù)端alert出來(lái)
????????appId:?wxJsApi.signPackage.appId,???//公眾號(hào)的唯一標(biāo)識(shí)
????????timestamp:?wxJsApi.signPackage.timestamp,???//生成簽名的時(shí)間戳
????????nonceStr:?wxJsApi.signPackage.nonceStr,?//生成簽名的隨機(jī)串
????????signature:?wxJsApi.signPackage.signature,???//簽名
????????jsApiList:?['chooseImage',?'uploadImage']???//需要使用的JS接口列表?這里我用了選擇圖片和上傳圖片接口
????});
????//通過(guò)ready接口處理成功驗(yàn)證,config信息驗(yàn)證后會(huì)執(zhí)行ready方法,所有接口調(diào)用都必須在config接口獲得結(jié)果之后
????wx.ready(function(){
????????//得到上傳圖片按鈕
????????document.querySelector('#uploadImage').onclick?=?function()?{
????????????var?images?=?{localId:[],serverId:[]};
????????????//調(diào)用?拍照或從手機(jī)相冊(cè)中選圖接口
????????????wx.chooseImage({
????????????????success:?function(res)?{
????????????????????if?(res.localIds.length?!=?1)?{
????????????????????????alert('只能上傳一張圖片');
????????????????????????return;
????????????????????}
????????????????????//返回選定照片的本地ID列表
????????????????????iamges.localId?=?res.localIds;
????????????????????images.serverId?=?[];
????????????????????//上傳圖片函數(shù)
????????????????????function?upload()?{
????????????????????????//調(diào)用上傳圖片接口
????????????????????????wx.uploadImage({
????????????????????????????localId:?images.localId[0],?//?需要上傳的圖片的本地ID,由chooseImage接口獲得
????????????????????????????isShowProcess:?1,???//?默認(rèn)為1,顯示進(jìn)度提示
????????????????????????????success:?function(res)?{
????????????????????????????????//返回圖片的服務(wù)器端ID?res.serverId,然后調(diào)用wxImgCallback函數(shù)進(jìn)行下載圖片操作
????????????????????????????????wxImgCallback(res.serverId);
????????????????????????????},
????????????????????????????fail:?function(res)?{
????????????????????????????????alert('上傳失敗');
????????????????????????????}
????????????????????????});
????????????????????}
????????????????????upload();
????????????????}
????????????});
????????}
????});
}
function?wxImgCallback(serverId)?{
????//將serverId傳給wx_upload.php的upload方法
????var?url?=?'wx_upload/upload/'+serverId;
????$.getJSON(url,?function(data){
????????if?(data.code?==?0)?{
????????????alert(data.msg);
????????}?else?if?(data.code?==?1)?{
????????????//存儲(chǔ)到服務(wù)器成功后的處理
????????????//
????????}
????});
}
本次講解就到此,這篇博文是給對(duì)微信接口開(kāi)發(fā)有興趣的朋友參考,如果你是高手,完全可以繞道。
?更多微信JS-SDK之圖像接口開(kāi)發(fā)相關(guān)文章請(qǐng)關(guān)注PHP中文網(wǎng)!