likes
comments
collection
share

微信JS-SDK说明文档 之调用扫一扫 、拍照上传图片功能

作者站长头像
站长
· 阅读数 15

微信JS-SDK是什么?

微信JS-SDK是微信公众平台 面向网页开发者提供的基于微信内的网页开发工具包。

通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照、选图、语音、位置等手机系统的能力,同时可以直接使用微信分享、扫一扫、卡券、支付等微信特有的能力,为微信用户提供更优质的网页体验。

一图胜前言,看下面图就知道了 微信JS-SDK说明文档 之调用扫一扫 、拍照上传图片功能

验证配置

将文件MP_verify_wah0fNzTG5YZlfMB.txt(点击下载) 放置在webapps 跟目下(springboot放置在resourece下即可)

先启动服务,然后再微信公众平台进行配置即可,成功的话 上面会显示 配置成功,我这里已经显示配置成功。

微信JS-SDK说明文档 之调用扫一扫 、拍照上传图片功能

2 调用顺序

官方文档: mp.weixin.qq.com/wiki?t=reso…

目录

1 概述

1.1 JSSDK使用步骤

1.1.1 步骤一:绑定域名

1.1.2 步骤二:引入JS文件

1.1.3 步骤三:通过config接口注入权限验证配置

1.1.4 步骤四:通过ready接口处理成功验证

1.1.5 步骤五:通过error接口处理失败验证

1.2 接口调用说明

2 基础接口

2.1 判断当前客户端版本是否支持指定JS接口
1 首先 需要绑定自己js 回调域名

先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。 备注:登录后可在“开发者中心”查看对应的接口权限。

2 引入JS文件

在需要调用JS接口的页面引入如下JS文件,(支持https):res.wx.qq.com/open/js/jwe… 如需进一步提升服务稳定性,当上述资源不可访问时,可改访问:res2.wx.qq.com/open/js/jwe… (支持>https)。 备注:支持使用 AMD/CMD 标准模块加载方法加载

直接在页面引用即可!

<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"> </script>
3 通过config接口注入权限验证配置

所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用,目前Android微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题会在Android6.2中修复)。

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

注(以下方法逻辑必须放在服务器端,放置在域中在前台获取):

附录1-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):…/15/54ce45d8d30b6bf6758f68d2e95bc627.html

2.用第一步拿到的access_token 采用http GET方式请求获得jsapi_ticket(有效期7200秒,开发者必须在自己的服务全局缓存jsapi_ticket):api.weixin.qq.com/cgi-bin/tic…

成功返回如下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=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=mp.weixin.qq.com?params=value

步骤2.

对string1进行sha1签名,得到signature:

0f9de62fce790f9a083d5c99e95740ceb90c27ed

注意事项

1.签名用的noncestr和timestamp必须与wx.config中的nonceStr和timestamp相同。

2.签名用的url必须是调用JS接口页面的完整URL。

3.出于安全考虑,开发者必须在服务器端实现签名的逻辑。

如出现invalid signature 等错误详见附录5常见错误及解决办法。

代码示例如下:

前提是access_token 已经获取,并且储存起来。

此代码直接复制即可 ( 出处: blog.csdn.net/u011327333/… ) 稍加修改,感谢此博主! 获取 后台 controller

    //微信jssdk 签名获取
    @GetMapping("getSignature")
    @ResponseBody
    public String getSignature(String url) {
        Map<String, String> sign  = JsSignUtil.sign(url);

        return JSON.toJSONString(sign);
    }
@Slf4j
public class JsSignUtil {

    public static Map<String, String> sign(String url) {

        String jsapi_ticket = JsapiTicketTool.getTicket();
        Map<String, String> ret = new HashMap<String, String>();
        String nonce_str = create_nonce_str();
        String timestamp = create_timestamp();
        String string1;
        String signature = "";

        //注意这里参数名必须全部小写,且必须有序
//        string1 = "jsapi_ticket=" + jsapi_ticket +
//                "&noncestr=" + nonce_str +
//                "&timestamp=" + timestamp +
//                "&url=" + url;

        string1 = getString1( nonce_str, timestamp, jsapi_ticket, url);

        log.info("=========== 拼接的 参数 string1 = {}", string1);
        signature = Sha1Util.getSha1(string1);
        log.info("=========== 解密的 signature = {}", signature);
 

        ret.put("url", url);
        ret.put("jsapi_ticket", jsapi_ticket);
        ret.put("nonceStr", nonce_str);
        ret.put("timestamp", timestamp);
        ret.put("signature", signature);
        ret.put("appId", WeChatConfig.appid);

        log.info("1.ticket(原始)=" + jsapi_ticket);
        log.info("2.url=" + ret.get("url"));
        log.info("3.jsapi_ticket(处理后)=" + ret.get("jsapi_ticket"));
        log.info("4.nonceStr=" + ret.get("nonceStr"));
        log.info("5.signature=" + ret.get("signature"));
        log.info("6.timestamp=" + ret.get("timestamp"));

        return ret;
    }



    public static String getSign(String jsapi_ticket, String noncestr, Long timestamp, String url)
            throws NoSuchAlgorithmException {
        String shaStr = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + noncestr + "×tamp=" + timestamp + "&url="
                + url;

        MessageDigest mDigest = MessageDigest.getInstance("SHA1");

        byte[] result = mDigest.digest(shaStr.getBytes());
        StringBuffer signature = new StringBuffer();
        for (int i = 0; i < result.length; i++) {
            signature.append(Integer.toString((result[i] & 0xff) + 0x100, 16).substring(1));
        }


        return signature.toString();
    }


    private static String getString1(String nonce_str, String timestamp, String jsapi_ticket, String url){
        //1.定义数组存放nonce_str,timestamp,jsapi_ticket,url
        String[] arr = {"noncestr="+nonce_str,"timestamp="+timestamp,"jsapi_ticket="+jsapi_ticket,"url="+url};
        //2.对数组进行排序
        Arrays.sort(arr);
        //3.生成字符串
        StringBuffer sb = new StringBuffer();
        for(String s : arr){
            sb.append(s);
            sb.append("&");
        }
        sb.deleteCharAt(sb.length()-1);
        return sb.toString();
    }

    /**
     * 随机加密
     * @param hash
     * @return
     */
    private static String byteToHex(final byte[] hash) {
        Formatter formatter = new Formatter();
        for (byte b : hash) {
            formatter.format("%02x", b);
        }
        String result = formatter.toString();
        formatter.close();
        return result;
    }

    /**
     * 产生随机串--由程序自己随机产生
     * @return
     */
    private static String create_nonce_str() {
        return UUID.randomUUID().toString();
    }

    /**
     * 由程序自己获取当前时间
     * @return
     */
    private static String create_timestamp() {
        return Long.toString(System.currentTimeMillis() / 1000);
    }


    /** 获取输入流
     * 获取临时素材 调用js-sdk微信上传图片后下载临时素材
     */
    public static InputStream getMediaStream(String mediaId) throws IOException {
        String url = WeChatUrlParamConfig.LIN_SHI_IMG_URL;
        String access_token = AccessTokenTool.getToken();
        String params = "access_token=" + access_token + "&media_id=" + mediaId;
        InputStream is = null;
        try {
            String urlNameString = url + "?" + params;
            URL urlGet = new URL(urlNameString);
            HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
            http.setRequestMethod("GET"); // 必须是get方式请求
            http.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
            http.setDoOutput(true);
            http.setDoInput(true);
            http.connect();
            // 获取文件转化为byte流
            is = http.getInputStream();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return is;

    }


}

4 通过ready接口处理成功验证

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

//1 如果在此处调用 拍照 ,支付 扫码等事件,会在页面加载时候进行弹出,调用js-sdk事件。 
//2 如不需要的话,请在外面调用,通过点击事件调用即可。
});

5 通过error接口处理失败验证

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

接口调用说明 所有接口通过wx对象(也可使用jWeixin对象)来调用,参数是一个对象,除了每个接口本身需要传的参数之外,还有以下通用参数:

1.success:接口调用成功时执行的回调函数。 2.fail:接口调用失败时执行的回调函数。 3.complete:接口调用完成时执行的回调函数,无论成功或失败都会执行。 4.cancel:用户点击取消时的回调函数,仅部分有用户取消操作的api才会用到。 5.trigger: 监听Menu中的按钮点击时触发的方法,该方法仅支持Menu中的相关接口。 备注:不要尝试在trigger中使用ajax异步请求修改本次分享的内容,因为客户端分享操作是一个同步操作,这时候使用ajax的回包会还没有返回。 以上几个函数都带有一个参数,类型为对象,其中除了每个接口本身返回的数据之外,还有一个通用属性errMsg,其值格式如下: 调用成功时:“xxx:ok” ,其中xxx为调用的接口名 用户取消时:“xxx:cancel”,其中xxx为调用的接口名 调用失败时:其值为具体错误信息

在页面中调取js-sdk 微信原生接口时候,必须判断微信客户端版本信息是否支持。

基础接口

判断当前客户端版本是否支持指定JS接口(此处建议放置在页面加载时函数体中)
wx.checkJsApi({
    jsApiList: ['chooseImage'], // 需要检测的JS接口列表,所有JS接口列表见附录2,
    success: function(res) {
        // 以键值对的形式返回,可用的api值true,不可用为false
        // 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
    }
});

备注:checkJsApi接口是客户端6.0.2新引入的一个预留接口,第一期开放的接口均可不使用checkJsApi来检测。

微信扫一扫 调起微信扫一扫接口

wx.scanQRCode({
    needResult: 0, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
    scanType: ["qrCode","barCode"], // 可以指定扫二维码还是一维码,默认二者都有
    success: function (res) {
    var result = res.resultStr; // 当needResult 为 1 时,扫码返回的结果
}
});

在页面中直接引用:

<script  type="text/javascript">
 $(document).ready(function () {
       init();
   });

 var jsApiList = [
        'checkJsApi',
        'scanQRCode',
        'chooseImage',
        'uploadImage'
    ];

   function init() {
       alert("url = " + location.href.split("#")[0])
       //获取签名 
       $.ajax({
           url:"${ctx}/wechat/getSignature",
           method:"post",
           data:{url:location.href.split("#")[0]},
           dataType: "json",
           success:function (data) {
               console.info("data = " , data);
               var appId = data['appId'];
               var timestamp = data['timestamp'];
               var nonceStr = data['nonceStr'];
               var signature = data['signature'];
          

                   //1 通过config接口注入权限验证配置
                   wx.config({
                       debug: true, ///  // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
                       appId: appId,
                       timestamp: timestamp,
                       nonceStr: nonceStr,
                       signature: signature,
                       jsApiList: jsApiList 
                   });

                    //2 通过ready接口处理成功验证
                   wx.ready(function () {
                           //check client vserion wether support JS API
                           wx.checkJsApi({
                               jsApiList: ['scanQRCode'],
                               success: function (res) {
                                   weui.alert("出错了:" + res.errMsg);
                               }
                           });
                     });

               //3 通过error接口处理失败验证
               //wrong error msg
               wx.error(function (res) {
                   weui.alert("出错了请稍后再试!" + res.errMsg);
               });
               },
           error:function (data) {
               weui.alert("网络异常,请稍后再试。");
           }
        });
   }

        //scanning Qr code
         function scanQRCodeFun() {
          
            wx.scanQRCode({
                needResult: 1, // 默认为0,扫描结果由微信处理,1则直接返回扫描结果,
                scanType: ["qrCode", "barCode"], // 可以指定扫二维码还是一维码,默认二者都有
                success: function (res) {

                    // query invoices msg  according scanning Qr code result
                    .... this is  yourselef  logic code ....



                }
            });
        };



    function photoFun() {
        wx.chooseImage({
            count: 1, // 最多可以选择的图片张数,默认9
            sizeType: ['original', 'compressed'],//图片的类型:原图,压缩图
            sourceType: ['album', 'camera'],  //图片来源:相册,拍照
            success: function (res) {
                var localIds = res.localIds; // 返回选定照片的本地ID列表(手机上操作就是手机端的ID列表,是一个数组),localId可以作为img标签的src属性显示图片
                wx.uploadImage({
                    localId: localIds[0], // 需要上传的图片的本地ID,由chooseImage接口获得
                    isShowProgressTips: 1, // 默认为1,显示进度提示
                    success: function (result) {
                        var serverId = result.serverId; // 返回图片的服务器端ID
                         //................. 执行逻辑
                    },
                    error: function () {
                    }
                });
            },
            fail: function () {
                alert("图片识别错误,请稍后再试!");
            },
            complete: function () {
                // alert("complete");
            }, cancel: function () {
            }
        });
    }


</script>

html页面

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=0,viewport-fit=cover">
    <link rel="stylesheet" href="https://res.wx.qq.com/open/libs/weui/2.0.0/weui.min.css">
    <script src="http://res.wx.qq.com/open/js/jweixin-1.4.0.js"></script>
    <script src="https://cdnjs.gtimg.com/cdnjs/libs/zepto/1.1.4/zepto.min.js"></script>
    <script src="https://res.wx.qq.com/open/libs/weuijs/1.1.4/weui.min.js"></script>
    <title>title</title>
</head>
<body ontouchstart>
<div class="weui-toptips weui-toptips_warn js_tooltips">错误提示</div>
<div class="page">
    <br>
    <div class="page__bd page__bd_spacing">
        <ul>
            <li>
                <div class="button-sp-area cell">
                    <br>
                    <button onclick="photoFun();" class="weui-btn weui-btn_block weui-btn_primary">拍照</button>

                    <br>
                    <button onclick="scanQRCodeFun();" class="weui-btn weui-btn_block weui-btn_primary">扫码</button>
                    <br>
                </div>
            </li>
        </ul>
    </div>
</div>


<div class="weui-footer weui-footer_fixed-bottom">
    <p class="weui-footer__text">Copyright © xxxxxx 有限公司</p>
</div>
</div>
</body>

更多js-sdk api接口 mp.weixin.qq.com/wiki?t=reso…