美文网首页
Springboot 页面调取微信拍照或从手机相册中选图接口

Springboot 页面调取微信拍照或从手机相册中选图接口

作者: 啊猪啊 | 来源:发表于2019-04-11 12:50 被阅读0次

    官方具体文档
    https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115
    之前使用的是input标签来打开摄像头,但是在微信里面有兼容性问题,索性就用微信官方的

    准备工作

    测试账号

    前去微信公众平台申请个测试账号,获取appID和appsecret,具体配置请去我的另一篇文章
    https://www.jianshu.com/p/7e8b1c2b031d
    appID、appsecret和后台代码配置完成之后,需要在微信测试账号管理中修改JS接口安全域名,图中红框内容替换成自己本地的内网穿透的域名

    20190410182204.png

    正式工作

    打开摄像头或相册
        <button type="button" onclick="getConfig()" class="chooseImage">选择</button>
        // 选择图片
        function getConfig() {
            var url = "/config"; //该地址为后台鉴权配置地址
            //url(当前网页的URL,不包含#及其后面部分)
            var pathUrl = window.location.href.split('#')[0];
            mui.get(url, {url: pathUrl}, function (data) {
                    //获得服务器响应
                    //步骤三:通过config接口注入权限验证配置
                    wx.config({
                        debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
                        appId: data.appId,   // 必填,公众号的唯一标识
                        timestamp: data.timestamp, // 必填,生成签名的时间戳
                        nonceStr: data.nonceStr, // 必填,生成签名的随机串
                        signature: data.signature,// 必填,签名,见附录1
                        jsApiList: ["chooseImage", "previewImage", "uploadImage", "downloadImage"] 
                        // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
                    });
    
                    // 步骤四:通过ready接口处理成功验证
                    wx.ready(function () {
                        // mui.alert("wx.config success.");
                        wx.checkJsApi({
                            jsApiList: [
                                'chooseImage',
                                'previewImage',
                                'uploadImage',
                                'downloadImage'
                            ],
                            success: function (res) {
                                if (res.checkResult.getLocation == false) {
                                    alert('你的微信版本太低,不支持微信JS接口,请升级到最新的微信版本!');
                                    return;
                                } else {
                                    wxChooseImage();
                                }
                            }
                        });
                    });
    
                    wx.error(function (res) {
                        alert("wx.config failed.");
                    });
                }, 'json'
            );
        };
        var images = {
            localId: [],
            serverId: []
        };
    
        //拍照或从手机相册中选图接口
        function wxChooseImage() {
            wx.chooseImage({
                count: 1, // 默认9
                sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
                success: function (res) {
                    images.localId = res.localIds;
                    if (images.localId.length == 0) {
                        alert('请先使用 chooseImage 接口选择图片');
                        return;
                    }
                    var i = 0, length = images.localId.length;
                    images.serverId = [];
    
                    wx.getLocalImgData({
                        localId: images.localId[0],//图片的本地ID
                        success: function (res) {
                            var localData = res.localData;
                            if (localData.indexOf('data:image') != 0) {
                                //判断是否有这样的头部
                                localData = 'data:image/jpeg;base64,' + localData
                            }
                            base64Img = localData.replace(/\r|\n/g, '').replace('data:image/jgp', 'data:image/jpeg');
                            $("#face_img").attr('src', base64Img);  // 选择的图片在页面的回调显示
                        }
                    })
                }
            });
        }
    

    后台 /config 接口:
    返回给前端 appId,timestamp,nonceStr,signature,前端拿着这个值去微信服务器请求验证,验证通过了,才能进行后续工作

    @Controller
    public class WXConntroller {
    
        // 微信的access_token分为两种,微信网页授权是通过OAuth2.0机制实现的,在用户授权给公众号后,
        // 公众号可以获取到一个网页授权特有的接口调用凭证(网页授权access_token),通过网页授权access_token可以进行授权后接口调用,
        // 如获取用户基本信息。两种的请求路径不同,access_token有效期为2小时
        // 这里对请求回来的access_token做缓存,项目中写了个定时任务,每两小时执行一次
        // 进行刷新access_token
        public void getAccessToken() throws IOException {
            if (!redisUtil.hasKey("access_token")){
                redisUtil.set("access_token",WxUtil.getAccessToken(appid,appsecret),7140000); //存入redis,有效期119分钟
            }
        }
    
        @GetMapping("/config")
        @ResponseBody
        public Map<String, Object> config(@Param("url") String url) throws IOException, NoSuchAlgorithmException {
            Map<String, Object> subJsonMap = new HashMap<>();
    
            // 1.获取参数
            getAccessToken();
            String accessToken = redisUtil.get("access_token").toString();
            String ticket = WxUtil.getTicket(accessToken);
    
            Long timestamp = System.currentTimeMillis() / 1000;
            String nonceStr = WxUtil.getNonceStr();
            String sign = WxUtil.getSign(ticket, nonceStr, timestamp, url);
            subJsonMap.put("result", "1");
            subJsonMap.put("ticket", ticket);
            subJsonMap.put("timestamp", timestamp);
            subJsonMap.put("nonceStr", nonceStr);
            subJsonMap.put("appId", appid);
            subJsonMap.put("signature", sign);
            return subJsonMap;  // 将此信息返回给前端,微信会根据该信息进行验证sign签名,匹配正确,则通过,否则 不通过(达到上面步骤四)
            // 此时便可打开微信的摄像头以及相册
        }
    }
    

    阿里云的fastjson,WxUtil工具类中使用到

    <!-- 阿里 JSON -->
    <dependency>
           <groupId>com.alibaba</groupId>
           <artifactId>fastjson</artifactId>
           <version>1.2.47</version>
    </dependency>
    
    import com.alibaba.fastjson.JSONObject;
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.text.SimpleDateFormat;
    import java.util.Arrays;
    import java.util.Date;
    import java.util.Formatter;
    
    public class WxUtil {
    
        /**
         * 获取当前时间 yyyyMMddHHmmss
         */
        public static String getCurrTime() {
            Date now = new Date();
            SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
            String s = outFormat.format(now);
            return s;
        }
    
        /**
         * 排序方法
         *
         * @param token     Token
         * @param timestamp 时间戳
         * @param nonce     随机数
         * @return
         */
        public static String sort(String token, String timestamp, String nonce) {
            String[] strArray = {token, timestamp, nonce};
            Arrays.sort(strArray);
            StringBuilder sb = new StringBuilder();
            for (String str : strArray) {
                sb.append(str);
            }
    
            return sb.toString();
        }
    
        /**
         * 将字符串进行sha1加密
         *
         * @param str 需要加密的字符串
         * @return 加密后的内容
         */
        public static String sha1(String str) {
            try {
                MessageDigest digest = MessageDigest.getInstance("SHA-1");
                digest.update(str.getBytes());
                byte messageDigest[] = digest.digest();
                // 创建 16进制字符串
                StringBuffer hexString = new StringBuffer();
                // 字节数组转换为 十六进制 数
                for (int i = 0; i < messageDigest.length; i++) {
                    String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
                    if (shaHex.length() < 2) {
                        hexString.append(0);
                    }
                    hexString.append(shaHex);
                }
                return hexString.toString();
    
            } catch (NoSuchAlgorithmException e) {
                e.printStackTrace();
            }
            return "";
        }
    
        /**
         * 生成随机字符串
         */
        public static String getNonceStr() {
            String currTime = getCurrTime();
            String strTime = currTime.substring(8, currTime.length());
            String strRandom = buildRandom(4) + "";
            return strTime + strRandom;
        }
    
        /**
         * 获取公众号access_token
         *
         * @param appid
         * @param secret
         * @return
         * @throws IOException
         */
        public static String getAccessToken(String appid, String secret) throws IOException {
            String token;
            String token_url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + appid
                    + "&secret=" + secret;
            JSONObject result = HttpClientUtils.doGet(token_url);
            return result.get("access_token").toString();
        }
    
        /**
         * 获取微信ticket
         *
         * @param token
         * @return
         * @throws IOException
         */
        public static String getTicket(String token) throws IOException {
            if ("".equalsIgnoreCase(token) || null == token) {
                return "";
            }
            String ticket_url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + token + "&type=jsapi";
            JSONObject result = HttpClientUtils.doGet(ticket_url);
            return result.get("ticket").toString();
    
        }
    
    
        /**
         * 取出一个指定长度大小的随机正整数.
         *
         * @param length int 设定所取出随机数的长度。length小于11
         * @return int 返回生成的随机数。
         */
        public static int buildRandom(int length) {
            int num = 1;
            double random = Math.random();
            if (random < 0.1) {
                random = random + 0.1;
            }
            for (int i = 0; i < length; i++) {
                num = num * 10;
            }
            return (int) ((random * num));
        }
    
        /**
         * 获取签名
         *
         * @param jsapi_ticket
         * @param nonce_str
         * @param timestamp
         * @param url
         * @return
         * @throws UnsupportedEncodingException
         * @throws NoSuchAlgorithmException
         */
        public static String getSign(String jsapi_ticket, String nonce_str, Long timestamp, String url) throws UnsupportedEncodingException, NoSuchAlgorithmException {
            // 注意这里参数名必须全部小写,且必须有序
            String string1 = "jsapi_ticket=" + jsapi_ticket + "&noncestr=" + nonce_str
                    + "&timestamp=" + timestamp + "&url=" + url;
            MessageDigest crypt = MessageDigest.getInstance("SHA-1");
            crypt.reset();
            crypt.update(string1.getBytes("UTF-8"));
            String signature = byteToHex(crypt.digest());
            return signature;
        }
    
        public 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;
        }
    }
    
    图片上传服务器
    • chooseImage:拍照或从手机相册中选图接口
    • previewImage:预览图片接口
    • uploadImage:上传图片接口(上传到微信服务器,上传图片有效期3天,可用微信多媒体接口下载图片到自己的服务器,此处获得的 serverId 即 media_id。)
    • downloadImage:下载图片接口(从微信服务器下载)
    • getLocalImgData:获取本地图片接口(可根据chooseImage返回的localIds,配合该接口直接获取该图片的base64编码)
      注:getLocalImgData接口仅在 iOS WKWebview 下提供,用于兼容 iOS WKWebview 不支持 localId 直接显示图片的问题。意思就是getLocalImgData获取到图片的base64编码在ios端可以直接作为img标签的src属性值,在安卓端却不行使用如下代码解决:
        if (localData.indexOf('data:image') != 0) {
                //判断是否有这样的头部
               localData = 'data:image/jpeg;base64,' + localData
        }
        base64Img = localData.replace(/\r|\n/g, '').replace('data:image/jgp', 'data:image/jpeg');
    

    最后页面再上传即可。

    正式上线

    将springboot 打包放到服务器后,需要在
    https://mp.weixin.qq.com/advanced/advanced?action=dev&t=advanced/dev&token=1889838119&lang=zh_CN
    基本配置-公众号开发信息中,将服务器的IP配置到白名单中

    20190411124701.png
    最后需要在
    https://mp.weixin.qq.com/cgi-bin/settingpage?t=setting/function&action=function&token=1889838119&lang=zh_CN
    公众号设置-功能设置 将JS接口安全域名填写自己的域名
    20190411124901.png

    前后台分离的前端页面使用微信jssdk

    上面的配置是springboot+freemarker,前后台不分离的,所以在本地调试的时候,直接将微信JS接口安全域名填写为springboot访问freemarker的地址(freemarker被springboot代理了),但是前后台分离的html,在浏览器中未被代理前微信服务器是不能访问到的,所以我用的windows版的nginx代理了本地的html文件,nginx配置信息如下:

    server {
            listen 80;
            server_name 127.0.0.1;
            location / {
                root   E:\Project;  // html文件的绝对地址
                index  index.html;  // 代理时访问的首页
            }
    }
    

    将本地html页面代理后,还需要配置微信JS接口安全域名,本地测试的时候填写本地的ip地址就行了,线上的话还需要将鉴权文本放到你html页面存放的位置,然后JS接口安全域名填写线上的域名,后台就用上面springboot后台即可。

    注意:
    • 不能使用hbuilder代理的html页面后,再用内网穿透将hbuilder代理的ip和端口号穿透到公网,这样是访问不了的,hbuilder代理的时候内部做了限制
    • 微信jssdk 只能在微信环境下使用
    转载还请注明出处

    相关文章

      网友评论

          本文标题:Springboot 页面调取微信拍照或从手机相册中选图接口

          本文链接:https://www.haomeiwen.com/subject/kekyjqtx.html