我這裡借助微信JSSDK的圖像介面對(duì)其進(jìn)行開發(fā)實(shí)現(xiàn)圖片上傳的功能,為何我選擇此介面?第一,目前的專案是在微信中打開的網(wǎng)頁,利用此接口,性能肯定是好一點(diǎn)的啦,畢竟是微信自己的東西;第二,用此接口,開發(fā)效率更高嘛;第三,最重要的一點(diǎn),就是它能對(duì)圖片進(jìn)行壓縮,假如一張2M的圖片,透過微信圖片上傳介面可以將圖片壓縮成幾百K的大小,這對(duì)網(wǎng)站的效能是很有幫助的。
一、我的想法是:
先呼叫「拍照或從手機(jī)相簿選擇圖片介面」—>選擇成功圖片後—>呼叫「上傳圖片介面」—>上傳成功後(也就是圖片上傳到了微信伺服器上)—>呼叫「下載圖片介面」—>將圖片下載到自己的伺服器儲(chǔ)存。
?
二、JSSDK的使用步驟
1、概述
微信JS-SDK是微信公眾平臺(tái)為網(wǎng)頁開發(fā)者提供的基於微信內(nèi)的網(wǎng)頁開發(fā)工具包。
透過使用微信JS-SDK,網(wǎng)頁開發(fā)者可藉助微信高效地使用拍照、選圖、語音、位置等手機(jī)系統(tǒng)的能力,同時(shí)可以直接使用微信分享、掃一掃、卡券、支付等微信特有的能力,提供微信使用者更優(yōu)質(zhì)的網(wǎng)頁體驗(yàn)。
2、使用步驟
步驟一:綁定網(wǎng)域名稱
先登入微信公眾平臺(tái)進(jìn)入「公眾號(hào)設(shè)定」的「功能設(shè)定」內(nèi)填寫「JS介面安全域名」。
備註:登入後可在「開發(fā)者中心」查看對(duì)應(yīng)的介面權(quán)限。
步驟二:引入JS檔案
在需要呼叫JS介面的頁面引入如下JS文件,(支援https):http://res.wx.qq.com/open/js /jweixin-1.0.0.js
步驟三:透過config介面注入權(quán)限驗(yàn)證設(shè)定
所有需要使用JS-SDK的頁面必須先註入設(shè)定訊息,否則將無法呼叫(同一個(gè)url只需呼叫一次,對(duì)於變化url的SPA的web app可在每次url變化時(shí)進(jìn)行呼叫)
wx.config({
????debug:?true,?//?開啟調(diào)試模式,調(diào)用的所有api的返回值會(huì)在客戶端alert出來,若要查看傳入的參數(shù),可以在pc端打開,參數(shù)信息會(huì)通過log打出,僅在pc端時(shí)才會(huì)打印。
????appId:?'',?//?必填,公眾號(hào)的唯一標(biāo)識(shí)
????timestamp:?,?//?必填,生成簽名的時(shí)間戳
????nonceStr:?'',?//?必填,生成簽名的隨機(jī)串
????signature:?'',//?必填,簽名,見附錄1
????jsApiList:?[]?//?必填,需要使用的JS接口列表,所有JS接口列表見附錄2
});
步驟四:透過ready介面處理成功驗(yàn)證
wx.ready(function(){
????//?config信息驗(yàn)證后會(huì)執(zhí)行ready方法,所有接口調(diào)用都必須在config接口獲得結(jié)果之后,config是一個(gè)客戶端的異步操作,所以如果需要在頁面加載時(shí)就調(diào)用相關(guān)接口,則須把相關(guān)接口放在ready函數(shù)中調(diào)用來確保正確執(zhí)行。對(duì)于用戶觸發(fā)時(shí)才調(diào)用的接口,則可以直接調(diào)用,不需要放在ready函數(shù)中。
});
步驟五:透過error介面處理失敗驗(yàn)證
wx.error(function(res){
????//?config信息驗(yàn)證失敗會(huì)執(zhí)行error函數(shù),如簽名過期導(dǎo)致驗(yàn)證失敗,具體錯(cuò)誤信息可以打開config的debug模式查看,也可以在返回的res參數(shù)中查看,對(duì)于SPA可以在這里更新簽名。
});
介面呼叫說明
所有介面透過wx物件(也可使用jWeixin物件)來調(diào)用,參數(shù)是一個(gè)對(duì)象,除了每個(gè)介面本身需要傳的參數(shù)之外,還有以下通用參數(shù):
success:介面呼叫成功時(shí)執(zhí)行的回呼函數(shù)。
fail:介面呼叫失敗時(shí)執(zhí)行的回呼函數(shù)。
complete:在介面呼叫完成時(shí)執(zhí)行的回呼函數(shù),無論成功或失敗都會(huì)執(zhí)行。
cancel:使用者點(diǎn)擊取消時(shí)的回呼函數(shù),只有部分有使用者取消操作的api才會(huì)用到。
trigger: 監(jiān)聽Menu中的按鈕點(diǎn)擊時(shí)觸發(fā)的方法,該方法僅支援Menu中的相關(guān)介面。
備註:不要嘗試在trigger中使用ajax非同步要求修改本次分享的內(nèi)容,因?yàn)榭蛻舳朔窒聿僮魇且粋€(gè)同步操作,這時(shí)候使用ajax的回包會(huì)還沒有返回。
以上幾個(gè)函數(shù)都帶有一個(gè)參數(shù),類型為對(duì)象,其中除了每個(gè)接口本身返回的資料之外,還有一個(gè)通用屬性errMsg,其值格式如下:
呼叫成功時(shí):"xxx:ok" ,其中xxx為呼叫的介面名稱
使用者取消時(shí):"xxx:cancel" ,其中xxx為呼叫的介面名稱
呼叫失敗時(shí):其值為具體錯(cuò)誤訊息
##
# 三、開發(fā)及程式碼分析詳解(用的是CI框架,只要是MVC模式都可以)
1、先在伺服器端取到:公眾號(hào)碼的唯一識(shí)別appId、產(chǎn)生簽章的時(shí)間戳timestamp 、產(chǎn)生簽名的隨機(jī)串nonceStr、簽名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形式傳到模板頁面
????????$this->smartyData['wxJsApi']?=?json_encode(array('signPackage'?=>?$this->model->weixin->signPackage()));
????}
###圖片上傳控制器###
<?php
class WxModel extends ModelBase{
public $appId;
public $appSecret;
public $token;
public function __construct() {
parent::__construct();
//審核通過的移動(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)頁的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ù)名均為小寫字符
????????????$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();
????????????????//通過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;
????????}
###取得到appId、nonceStr、timestamp、signature模型###### 這裡要補(bǔ)充一些JS-SDK使用權(quán)限簽章演算法的想法與注意點(diǎn)(這裡我直接複製官網(wǎng)文件給大家看看)###### jsapi_ticket###### 生成簽章之前必須先了解一下jsapi_ticket,jsapi_ticket是公眾號(hào)碼用於使用微信的簽名之前必須先了解一下jsapi_ticket,jsapi_ticket是公眾號(hào)碼用於使用微信正常情況下,jsapi_ticket的有效期限為7,200秒,透過access_token來取得。由於取得jsapi_ticket的api呼叫次數(shù)非常有限,頻繁刷新jsapi_ticket會(huì)導(dǎo)致api呼叫受限,影響自身業(yè)務(wù),開發(fā)者必須在自己的服務(wù)全域快取jsapi_ticket?。 ###### 1、獲取access_token(有效期7200秒,開發(fā)者必須在自己的服務(wù)全域快取access_token)###### 2、用第一步拿到的access_token 採(cǎi)用http GET方式請(qǐng)求(jsapi_ticket有效期限7200秒,開發(fā)者必須在自己的服務(wù)全域快取jsapi_ticket)###
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)頁的URL,不包含#及其后面部分) 。對(duì)所有待簽名參數(shù)按照字段名的ASCII 碼從小到大排序(字典序)后,使用URL鍵值對(duì)的格式(即key1=value1&key2=value2…)拼接成字符串string1。這里需要注意的是所有參數(shù)名均為小寫字符。對(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://ipnx.cn/
步驟2. 對(duì)string1進(jìn)行sha1簽名,得到signature:
0f9de62fce790f9a083d5c99e95740ceb90c27ed
注意事項(xiàng)
1.簽名用的noncestr和timestamp必須與wx.config中的nonceStr和timestamp相同。
2.簽名用的url必須是調(diào)用JS接口頁面的完整URL。
3.出于安全考慮,開發(fā)者必須在服務(wù)器端實(shí)現(xiàn)簽名的邏輯。
?
? 2、取到我們所需要的值后,就在js文件里面開始使用了
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);
????//通過config接口注入權(quán)限驗(yàn)證配置
????wx.config({
????????debug:?false,???//開啟調(diào)試模式,調(diào)用的所有api的返回值會(huì)在客戶端alert出來
????????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接口列表?這里我用了選擇圖片和上傳圖片接口
????});
????//通過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,然后通過這個(gè)來下載圖片到本地服務(wù)器
這里先補(bǔ)充下如何調(diào)用下載圖片接口(我直接復(fù)制官方文檔的說明了)
公眾號(hào)可調(diào)用本接口來獲取多媒體文件。請(qǐng)注意,視頻文件不支持下載,調(diào)用該接口需http協(xié)議。
接口調(diào)用請(qǐng)求說明
http請(qǐng)求方式:?GET
http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID
參數(shù)說明
參數(shù) |
是否必須 |
說明 |
access_token |
是 |
調(diào)用接口憑證 |
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"
錯(cuò)誤情況下的返回JSON數(shù)據(jù)包示例如下(示例為無效媒體ID錯(cuò)誤)::
{"errcode":40007,"errmsg":"invalid?media_id"}
接下來看自己寫的代碼
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);
????????????//文件類型
????????????$types?=?array('image/bmp'=>'.bmp',?'image/gif'=>'.gif',?'image/jpeg'=>'.jpg',?'image/png'=>'.png');
????????????//判斷響應(yīng)首部里的的content-type的值是否是這四種圖片類型
????????????if?(isset($types[$info['header']['content_type']]))?{
????????????????//文件的uri
????????????????$path?=?$path.$types[$info['header']['content_type']];
????????????}?else?{
????????????????return?false;
????????????}
????????????//將資源寫入文件里
????????????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;
????????}
????????/**
?????????*?將資源寫入文件
?????????*?@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)。我們用到了微信的選擇圖片接口、上傳圖片接口和下載媒體資源接口。
下面我附上這一接口開發(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形式傳到模板頁面
????????$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();
//審核通過的移動(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)頁的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ù)名均為小寫字符
????????????$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();
????????????????//通過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);
????????????//文件類型
????????????$types?=?array('image/bmp'=>'.bmp',?'image/gif'=>'.gif',?'image/jpeg'=>'.jpg',?'image/png'=>'.png');
????????????//判斷響應(yīng)首部里的的content-type的值是否是這四種圖片類型
????????????if?(isset($types[$info['header']['content_type']]))?{
????????????????//文件的uri
????????????????$path?=?$path.$types[$info['header']['content_type']];
????????????}?else?{
????????????????return?false;
????????????}
????????????//將資源寫入文件里
????????????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;
????????}
????????/**
?????????*?將資源寫入文件
?????????*?@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);
????//通過config接口注入權(quán)限驗(yàn)證配置
????wx.config({
????????debug:?false,???//開啟調(diào)試模式,調(diào)用的所有api的返回值會(huì)在客戶端alert出來
????????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接口列表?這里我用了選擇圖片和上傳圖片接口
????});
????//通過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ì)微信接口開發(fā)有興趣的朋友參考,如果你是高手,完全可以繞道。
?更多微信JS-SDK之圖像接口開發(fā)相關(guān)文章請(qǐng)關(guān)注PHP中文網(wǎng)!