WeChat JS-SDK 이미지 인터페이스 개발

高洛峰
풀어 주다: 2017-03-01 09:29:51
원래의
1800명이 탐색했습니다.

여기서는 WeChat JSSDK의 이미지 인터페이스를 사용하여 이미지 업로드 기능을 구현하도록 개발했습니다. 왜 이 인터페이스를 선택했나요? 첫째, 현재 프로젝트는 WeChat에서 열리는 웹페이지입니다. 결국에는 WeChat 자체의 성능이 향상됩니다. 둘째, 이 인터페이스를 사용하면 개발 효율성이 높아집니다. 중요한 점은 이미지를 압축할 수 있다는 점입니다. 예를 들어 2M 이미지를 사용하면 WeChat 이미지 업로드 인터페이스를 통해 이미지를 수백 K 크기로 압축할 수 있어 웹사이트 성능에 매우 도움이 됩니다.

1. 내 아이디어는 다음과 같습니다.

먼저 "사진 또는 휴대폰 앨범 인터페이스에서 사진 선택"을 호출합니다. -> 성공한 사진을 선택한 후 -> 인터페이스' -> 업로드가 성공한 후(즉, 이미지가 WeChat 서버에 업로드됨) -> "이미지 다운로드 인터페이스" 호출 -> 저장을 위해 이미지를 자체 서버에 다운로드합니다.

2. JSSDK 사용 단계

1. 개요

WeChat JS-SDK는 WeChat 웹 기반의 웹 개발자를 위해 WeChat 공개 플랫폼에서 제공됩니다. 개발 툴킷.

WeChat JS-SDK를 사용하면 웹 개발자는 WeChat을 사용하여 사진 촬영, 사진 선택, 음성 선택, 위치 등 휴대폰 시스템의 기능을 효율적으로 사용하는 동시에 WeChat을 직접 사용할 수 있습니다. 공유, 스캔, 쿠폰 등. 결제와 같은 WeChat의 고유한 기능은 WeChat 사용자에게 더 나은 웹 경험을 제공합니다.

2. 사용 단계

1단계: 도메인 이름 바인딩

먼저 WeChat 공개 플랫폼에 로그인하고 "공식 계정 설정"의 "기능 설정"으로 들어갑니다. "JS 인터페이스 보안" 도메인 이름을 입력합니다.

참고: 로그인 후 "개발자 센터"에서 해당 인터페이스 권한을 확인할 수 있습니다.

2단계: JS 파일 소개

JS 인터페이스를 호출해야 하는 페이지에 다음 JS 파일을 소개합니다(https 지원): http://res.wx.qq.com /open/js /jweixin-1.0.0.js

3단계: 구성 인터페이스를 통해 권한 확인 구성 주입

JS-SDK를 사용해야 하는 모든 페이지는 먼저 구성 정보를 주입해야 하며, 그렇지 않으면 호출되지 않습니다. (URL은 한 번만 호출하면 되고 URL이 변경될 때마다 URL을 변경하는 SPA 웹 앱을 호출할 수 있는 것과 같습니다.)

wx.config({
    debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
    appId: '', // 必填,公众号的唯一标识
    timestamp: , // 必填,生成签名的时间戳
    nonceStr: '', // 必填,生成签名的随机串
    signature: '',// 必填,签名,见附录1
    jsApiList: [] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
로그인 후 복사

4단계: 다음을 통해 성공적인 확인을 처리합니다. 준비된 인터페이스

wx.ready(function(){

    // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
});
로그인 후 복사

5단계: 오류 인터페이스를 통해 실패한 검증 처리

wx.error(function(res){

    // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。

});
로그인 후 복사

인터페이스 호출 지침

모든 인터페이스는 wx 개체를 통해 호출됩니다(jWeixin 개체도 사용할 수 있음). , 매개변수는 객체입니다. 단, 각 인터페이스 자체는 전달되어야 합니다. 매개변수 외에도 다음과 같은 일반 매개변수가 있습니다.

  1. success: 콜백 함수가 실행될 때 실행됩니다. 인터페이스 호출이 성공했습니다.

  2. fail: 인터페이스 호출이 실패했을 때 실행되는 콜백 함수입니다.

  3. 완료: 성공, 실패에 관계없이 인터페이스 호출이 완료되면 실행되는 콜백 함수입니다.

  4. cancel: 사용자가 취소를 클릭할 때의 콜백 함수는 사용자가 작업을 취소하는 경우에만 사용됩니다.

  5. trigger: 메뉴의 버튼을 클릭했을 때 실행되는 메서드입니다. 이 메서드는 메뉴의 관련 인터페이스만 지원합니다.

참고: 이 공유의 콘텐츠를 수정하기 위해 트리거에서 ajax 비동기 요청을 사용하지 마십시오. 클라이언트 공유 작업은 동기 작업이고 ajax를 사용하는 반환 패킷은 지금은 돌아올 수 없습니다.


위 함수에는 모두 객체 유형의 매개변수가 있습니다. 각 인터페이스 자체에서 반환되는 데이터 외에도 값 형식이 다음과 같은 일반 속성 errMsg도 있습니다.

    1. 호출이 성공한 경우: "xxx:ok", 여기서 xxx는 호출된 인터페이스 이름입니다.

    2. 사용자가 취소한 경우: "xxx: cancel" , 여기서 xxx는 호출된 인터페이스의 이름입니다.

    3. 호출이 실패하는 경우: 해당 값은 특정 오류 메시지입니다

 

3. 개발 및 코드 분석에 대한 자세한 설명(CI 프레임워크 사용, MVC 모드인 경우)

1. 먼저 서버측에서 획득: 고유 식별자 appId 공식 계정과 생성된 서명의 타임스탬프를 사용하여 서명된 임의 문자열 nonceStr 및 서명 서명을 생성합니다.

<?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');
        //取得:公众号的唯一标识appId、生成签名的时间戳timestamp、生成签名的随机串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();

            //审核通过的移动应用所给的AppID和AppSecret
            $this->appId = 'wx0000000000000000';
            $this->appSecret = '00000000000000000000000000000';
            $this->token = '00000000';
        }

        /**
         * 获取jssdk所需参数的所有值
         * @return array
         */
        public function signPackage() {
            $protocol = (!empty($_SERVER['HTTPS'] && $_SERVER['HTTPS'] == 'off' || $_SERVER['port'] == 443)) ? 'https://' : 'http://';
            //当前网页的URL
            $url = "$protocol$_SERVER['host']$_SERVER['REQUEST_URI']";
            //生成签名的时间戳
            $timestamp = time();
            //生成签名的随机串
            $nonceStr = $this->createNonceStr();
            //获取公众号用于调用微信JS接口的临时票据
            $jsApiTicket = $this->getJsApiTicket();
            //对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,
            //使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串$str。
            //这里需要注意的是所有参数名均为小写字符
            $str = "jsapi_ticket=$jsApiTicket&noncestr=$nonceStr&timestamp=$timestamp&url=$url";
            //对$str进行sha1签名,得到signature:
            $signature = sha1($str);
            $signPackage = array(
                "appId"     => $this->AppId,
                "nonceStr"  => $nonceStr,
                "timestamp" => $timestamp,
                "url"       => $url,
                "signature" => $signature,
                "rawString" => $string
                );
            return $signPackage;
        }

        /**
         * 创建签名的随机字符串
         * @param  int $length 字符串长度
         * @return string      随机字符串
         */
        private function createNonceStr($length == 16) {
            $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
            $str = '';
            for ($i=0; $i < $length; $i++) { 
                $str .= substr(mt_rand(0, strlen($chars)), 1);
            }
            return $str;
        }

        /**
         * 获取公众号用于调用微信JS接口的临时票据
         * @return string 
         */
        private function getJsApiTicket() {
            //先查看redis里是否存了jsapi_ticket此值,假如有,就直接返回
            $jsApiTicket = $this->library->redisCache->get('weixin:ticket');
            if (!$jsApiTicket) {
                //先获取access_token(公众号的全局唯一票据)
                $accessToken = $this->getApiToken();
                //通过access_token 采用http GET方式请求获得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里面,下次就不用再请求去取了
                $expire = max(1, intval($result['expire']) - 60);
                $this->library->redisCache->set('weixin:ticket', $jsApiTicket, $expire);
            }
            return $jsApiTicket;
        }

        /**
         * 获取众号的全局唯一票据access_token
         * @param  boolean $forceRefresh 是否强制刷新
         * @return string                返回access_token
         */
        private function getApiToken($forceRefresh = false) {
            //先查看redis是否存了accessToken,如果有了,就不用再去微信server去请求了(提高效率)
            $accessToken = $this->library->redisCache->get('weixin:accessToken');
            //强制刷新accessToken或者accessToken为空时就去请求accessToken
            if ($forceRefresh || empty($accessToken)) {
                //请求得到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);
                //将其存进redis里面去
                $this->library->redisCache->set('weixin:accessToken', $accessToken, $expire);
            }
            return $accessToken;
        }
로그인 후 복사

appId, nonceStr, 타임스탬프, 서명 모델 가져오기

다음은 JS-SDK에서 권한 서명 알고리즘 사용에 대한 몇 가지 아이디어와 참고 사항입니다( 여기서는 모두가 볼 수 있도록 공식 웹사이트 문서를 직접 복사합니다.)

jsapi_ticket

서명을 생성하기 전에 먼저 jsapi_ticket이 WeChat을 호출하기 위해 공용 계정에서 사용하는 임시 티켓이라는 점을 이해해야 합니다. 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:

{
"errcode":0,
"errmsg":"ok",
"ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA",
"expires_in":7200
}
로그인 후 복사

    获得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://www.php.cn/
로그인 후 복사

    步骤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

if(typeof($util)=='undefined')$util={};

$.util.wxMenuImage = function(json) {
    if (json.length == 0) return; 
    //解析json变成js对象
    wxJsApi = JSON.parse(json);

    //通过config接口注入权限验证配置
    wx.config({
        debug: false,   //开启调试模式,调用的所有api的返回值会在客户端alert出来
        appId: wxJsApi.signPackage.appId,   //公众号的唯一标识
        timestamp: wxJsApi.signPackage.timestamp,   //生成签名的时间戳
        nonceStr: wxJsApi.signPackage.nonceStr, //生成签名的随机串
        signature: wxJsApi.signPackage.signature,   //签名
        jsApiList: ['chooseImage', 'uploadImage']   //需要使用的JS接口列表 这里我用了选择图片和上传图片接口
    });

    //通过ready接口处理成功验证,config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后
    wx.ready(function(){
        //得到上传图片按钮
        document.querySelector('#uploadImage').onclick = function() {
            var images = {localId:[],serverId:[]};
            //调用 拍照或从手机相册中选图接口
            wx.chooseImage({
                success: function(res) {
                    if (res.localIds.length != 1) {
                        alert('只能上传一张图片');
                        return;
                    }
                    //返回选定照片的本地ID列表
                    iamges.localId = res.localIds;
                    images.serverId = [];
                    //上传图片函数
                    function upload() {
                        //调用上传图片接口
                        wx.uploadImage({
                            localId: images.localId[0], // 需要上传的图片的本地ID,由chooseImage接口获得
                            isShowProcess: 1,   // 默认为1,显示进度提示
                            success: function(res) {
                                //返回图片的服务器端ID res.serverId,然后调用wxImgCallback函数进行下载图片操作
                                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) {
            //存储到服务器成功后的处理
            //
        }
    });
}

图片选择和图片上传接口调用
로그인 후 복사

图片选择和图片上传接口调用

   

  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
로그인 후 복사
/*********************图片下载到本地服务器****************************************/
    //从微信服务器读取图片,然后下载到本地服务器
    public function upload($media_id) {
        //图片文件名
        $fileName = md5($this->wxId."/$media_id");
        //调用下载图片接口,返回路径
        $path = $this->weixin->wxDownImg($media_id, sys_get_temp_dir()."$fileName");
        if ($path != false) {
            //将图片的路径插入数据库去存储
            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')));
        }
        
    }
로그인 후 복사

从微信服务器下载图片到本地存储

//从微信服务器端下载图片到本地服务器
        public function wxDownImg($media_id, $path) {
            //调用 多媒体文件下载接口
            $url = "https://api.weixin.qq.com/cgi-bin/media/get?access_token={$this->model->weixin->_getApiToken()}&media_id=$media_id";
            //用curl请求,返回文件资源和curl句柄的信息
            $info = $this->curl_request($url);
            //文件类型
            $types = array('image/bmp'=>'.bmp', 'image/gif'=>'.gif', 'image/jpeg'=>'.jpg', 'image/png'=>'.png');
            //判断响应首部里的的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请求资源
         * @param  string $url 请求url
         * @return array 
         */
        private function curl_request($url = '') {
            if ($url == '') return;
            $ch = curl_init();
            //这里返回响应报文时,只要body的内容,其他的都不要
            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;
        }
로그인 후 복사

从微信服务器下载图片到本地存储接口

  到这里,已经完成了:

    先调用“拍照或从手机相册选择图片接口”—>选择成功图片后—>调用“上传图片接口”—>上传成功后(也就是图片上传到了微信服务器上)—>调用“下载图片接口”—>将图片下载到自己的服务器存储。

  这一思路的实现。我们用到了微信的选择图片接口、上传图片接口和下载媒体资源接口。

  下面我附上这一接口开发的全部代码:

<?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');
        //取得:公众号的唯一标识appId、生成签名的时间戳timestamp、生成签名的随机串nonceStr、签名signature这些值,并以json形式传到模板页面
        $this->smartyData['wxJsApi'] = json_encode(array('signPackage' => $this->model->weixin->signPackage()));
    }

    /*********************图片下载到本地服务器****************************************/
    //从微信服务器读取图片,然后下载到本地服务器
    public function upload($media_id) {
        //图片文件名
        $fileName = md5($this->wxId."/$media_id");
        //调用下载图片接口,返回路径
        $path = $this->weixin->wxDownImg($media_id, sys_get_temp_dir()."$fileName");
        if ($path != false) {
            //将图片的路径插入数据库去存储
            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();

            //审核通过的移动应用所给的AppID和AppSecret
            $this->appId = 'wx0000000000000000';
            $this->appSecret = '00000000000000000000000000000';
            $this->token = '00000000';
        }

        /**
         * 获取jssdk所需参数的所有值
         * @return array
         */
        public function signPackage() {
            $protocol = (!empty($_SERVER['HTTPS'] && $_SERVER['HTTPS'] == 'off' || $_SERVER['port'] == 443)) ? 'https://' : 'http://';
            //当前网页的URL
            $url = "$protocol$_SERVER['host']$_SERVER['REQUEST_URI']";
            //生成签名的时间戳
            $timestamp = time();
            //生成签名的随机串
            $nonceStr = $this->createNonceStr();
            //获取公众号用于调用微信JS接口的临时票据
            $jsApiTicket = $this->getJsApiTicket();
            //对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,
            //使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串$str。
            //这里需要注意的是所有参数名均为小写字符
            $str = "jsapi_ticket=$jsApiTicket&noncestr=$nonceStr&timestamp=$timestamp&url=$url";
            //对$str进行sha1签名,得到signature:
            $signature = sha1($str);
            $signPackage = array(
                "appId"     => $this->AppId,
                "nonceStr"  => $nonceStr,
                "timestamp" => $timestamp,
                "url"       => $url,
                "signature" => $signature,
                "rawString" => $string
                );
            return $signPackage;
        }

        /**
         * 创建签名的随机字符串
         * @param  int $length 字符串长度
         * @return string      随机字符串
         */
        private function createNonceStr($length == 16) {
            $chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
            $str = '';
            for ($i=0; $i < $length; $i++) { 
                $str .= substr(mt_rand(0, strlen($chars)), 1);
            }
            return $str;
        }

        /**
         * 获取公众号用于调用微信JS接口的临时票据
         * @return string 
         */
        private function getJsApiTicket() {
            //先查看redis里是否存了jsapi_ticket此值,假如有,就直接返回
            $jsApiTicket = $this->library->redisCache->get('weixin:ticket');
            if (!$jsApiTicket) {
                //先获取access_token(公众号的全局唯一票据)
                $accessToken = $this->getApiToken();
                //通过access_token 采用http GET方式请求获得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里面,下次就不用再请求去取了
                $expire = max(1, intval($result['expire']) - 60);
                $this->library->redisCache->set('weixin:ticket', $jsApiTicket, $expire);
            }
            return $jsApiTicket;
        }

        /**
         * 获取众号的全局唯一票据access_token
         * @param  boolean $forceRefresh 是否强制刷新
         * @return string                返回access_token
         */
        private function getApiToken($forceRefresh = false) {
            //先查看redis是否存了accessToken,如果有了,就不用再去微信server去请求了(提高效率)
            $accessToken = $this->library->redisCache->get('weixin:accessToken');
            //强制刷新accessToken或者accessToken为空时就去请求accessToken
            if ($forceRefresh || empty($accessToken)) {
                //请求得到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);
                //将其存进redis里面去
                $this->library->redisCache->set('weixin:accessToken', $accessToken, $expire);
            }
            return $accessToken;
        }

        //从微信服务器端下载图片到本地服务器
        public function wxDownImg($media_id, $path) {
            //调用 多媒体文件下载接口
            $url = "https://api.weixin.qq.com/cgi-bin/media/get?access_token={$this->model->weixin->_getApiToken()}&media_id=$media_id";
            //用curl请求,返回文件资源和curl句柄的信息
            $info = $this->curl_request($url);
            //文件类型
            $types = array('image/bmp'=>'.bmp', 'image/gif'=>'.gif', 'image/jpeg'=>'.jpg', 'image/png'=>'.png');
            //判断响应首部里的的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请求资源
         * @param  string $url 请求url
         * @return array 
         */
        private function curl_request($url = '') {
            if ($url == '') return;
            $ch = curl_init();
            //这里返回响应报文时,只要body的内容,其他的都不要
            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">点击上传图片</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对象
    wxJsApi = JSON.parse(json);

    //通过config接口注入权限验证配置
    wx.config({
        debug: false,   //开启调试模式,调用的所有api的返回值会在客户端alert出来
        appId: wxJsApi.signPackage.appId,   //公众号的唯一标识
        timestamp: wxJsApi.signPackage.timestamp,   //生成签名的时间戳
        nonceStr: wxJsApi.signPackage.nonceStr, //生成签名的随机串
        signature: wxJsApi.signPackage.signature,   //签名
        jsApiList: ['chooseImage', 'uploadImage']   //需要使用的JS接口列表 这里我用了选择图片和上传图片接口
    });

    //通过ready接口处理成功验证,config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后
    wx.ready(function(){
        //得到上传图片按钮
        document.querySelector('#uploadImage').onclick = function() {
            var images = {localId:[],serverId:[]};
            //调用 拍照或从手机相册中选图接口
            wx.chooseImage({
                success: function(res) {
                    if (res.localIds.length != 1) {
                        alert('只能上传一张图片');
                        return;
                    }
                    //返回选定照片的本地ID列表
                    iamges.localId = res.localIds;
                    images.serverId = [];
                    //上传图片函数
                    function upload() {
                        //调用上传图片接口
                        wx.uploadImage({
                            localId: images.localId[0], // 需要上传的图片的本地ID,由chooseImage接口获得
                            isShowProcess: 1,   // 默认为1,显示进度提示
                            success: function(res) {
                                //返回图片的服务器端ID res.serverId,然后调用wxImgCallback函数进行下载图片操作
                                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) {
            //存储到服务器成功后的处理
            //
        }
    });
}
로그인 후 복사

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

 更多微信JS-SDK之图像接口开发相关文章请关注PHP中文网!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
최신 이슈
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿