JAVA实现微信公众号调用摄像头拍照和打开本地相册上传图片至服务器

版权声明:本文为博主原创文章,如需转载,敬请注明转载链接 https://blog.csdn.net/guobinhui/article/details/82897904

一、主体实现流程

1.引入Jquery和微信公众号JS-SDK (<script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>)
点击button或div,通过Ajax请求后台自定义接口,获取微信拍照和打开本地相册接口所需的参数,调用拍照接口【chooseImage】和上传图片接口【uploadImage】,拿到图片所存储的微信服务器端ID,即【mediaId】
2.调用【获取临时素材】downloadImage()接口(参数mediaId),拿到返回的图片文件流(或其他格式的多媒体文件流),保存到自己的服务器。

二、具体实现步骤

1.绑定域名 
先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。 
如果暂时没有认证过的公众号,可以通过这个微信链接去注册一个测试公众账号,并配置JS接口安全域名,注意这里配置的JS接口安全域名不带http和www,格式比如 42du.net

测试公众账号申请地址 https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index

在本页面还要配置一下微信认证网页授权,注意授权回调页面域名不带http和www,格式比如 42du.net

2.在需要调用摄像头的页面,引入JS文件

<!-- jquery 非必须 -->

<script src="/common/js/jquery-2.2.4.min.js"></script>
<script src="/common/js/bootstrap.min.js"></script>
<script src="/common/js/bootstrap-select.min.js"></script>
<script src="/common/js/i18n/defaults-zh_CN.min.js"></script>
<script src="/common/js/ajax.js"></script>
<script src="/common/js/jquery.form.min.js"></script>
<!--需要调用摄像头-->
<script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>

注意:微信官网给到的 是jweixin-1.4.0.js不稳定,亲测不能用,jweixin-1.2.0.js可用

3.通过config接口注入权限验证配置

<script type="text/javascript">
  wx.config({
    debug: true,
    appId:res.appId, // 必填,公众号的唯一标识
    timestamp: res.timestamp, // 必填,生成签名的时间戳
    nonceStr: res.noncestr, // 必填,生成签名的随机串
    signature: res.signature,// 必填,签名,见附录1
    jsApiList: ['chooseImage','uploadImage','previewImage','downloadImage']// 必填
    });      
    </script>

4.JS-SDK使用权限签名算法,生成第三步中 config接口所需参数
注意:java代码使用springmvc框架,使用到的缓存、参数获取等语法,开发者自适应相应修改。

获取access_token(官方:有效期7200秒,开发者必须在自己的服务全局缓存access_token)、jsapi_ticket、签名sign

    /**
     * create by guo bin hui in 2018-09-28
     * 获取jsp页面调用微信公众号js-sdk上传图片的config接口所需参数
     */
    @RequestMapping(value = "/getWxConfig")
    @ResponseBody
    public Map getWxConfig(HttpSession seesion, HttpServletRequest request) throws IOException {
        Map <String,String> map = new HashMap <String,String> ();
        UserInfo  user = (UserInfo)request.getSession().getAttribute("user");
        String ticket = WeiXinUtil.getJsapiTicket(TEMP_ACCESS_TOKEN);
        map.put("jsapi_ticket", ticket);
        map.put("timestamp",WeiXinUtil.getTimestamp());
        map.put("noncestr",WxPayUtil.getNonceStr());
        map.put("url", Constants.JSSDKURL);
        String str =  WxPayUtil.createLinkString(map);
        String sign = DigestUtils.sha1Hex(str);
        map.put("signature",sign);
        map.put("appId",Constants.appID);
        map.put("userId",user.getUserId());
        return map;
    }

5.调用摄像头,获取 mediaId

<div class="col-xs-9 col-sm-9">
   <div class="img_container"></div>
   <div onclick="chooseImg()" style=" background-color: aquamarine;height: 200px;width: 200px;"></div>
</div>
<script type="text/javascript">
  function chooseImg(){
     $.ajax({
         url:"http://***.com/getWxConfig",
         dataType:"json",
         type:"post",
         success:function(res){
           wx.config({
              debug: true,
              appId:res.appId, // 必填,公众号的唯一标识
              timestamp: res.timestamp, // 必填,生成签名的时间戳
              nonceStr: res.noncestr, // 必填,生成签名的随机串
              signature: res.signature,// 必填,签名,见附录1
              jsApiList: ['chooseImage','uploadImage','previewImage','downloadImage']// 必填
                });
           wx.ready(function(){
              var localIds=[];
              var serverIds=[];
              wx.chooseImage({
                  count: 6, // 微信默认9
                  sizeType: ['original', 'compressed'],
                  sourceType: ['album', 'camera'],
                  success: function (res) {
                     serverIds=[];
                     medil_id="";
                     var imgUrl= "";
                     localIds = res.localIds;
                     alert('已选择 ' + res.localIds.length + ' 张图片');
                     if (res.localIds.length == 0) {
                         alert('请先使用微信的 chooseImage 接口选择图片');
                          return;
                     }
                     uploadImages(localIds);
                     for(var i=0;i<localIds.length;i++){
                          imgUrl+=localIds[i]+",";
                          $(".img_container").append('<div style="width:50%;padding: 0 5px;"><img width="100%" height="80px" style="margin-right: 10px;" src="'+localIds[i]+'"/></div>');}
                      },
                   fail:function(res){
                        alert("失败的原因"+res);
                     },
                   })
                   });
               },
               error:function(){
                   alert("上传失败");
               }
           })
        }
 
        function uploadImages(localImagesIds) {
            if (localImagesIds.length === 0) {
                $.showPreloader('正在提交数据...');
            }
            var localId = localImagesIds[0];
            //解决IOS无法上传的坑
            if (localId.indexOf("wxlocalresource") != -1) {
                localId = localId.replace("wxlocalresource", "wxLocalResource");
            }
            wx.uploadImage({
                localId: localId, // 需要上传的图片的本地ID,由chooseImage接口获得
                isShowProgressTips: 2, // 默认为1,显示进度提示
                success: function (res) {
                    var mediaId = res.serverId; // 返回图片的服务器端ID,即mediaId
                    //将获取到的 mediaId 传入后台 方法savePicture
                    $.post("<%=request.getContextPath()%>/my/savePicture",
                       {mediaId:mediaId},function(res){
                        if(res){
                            alert("上传成功!mediaId:"+mediaId);
                        }else{
                        }
                    })
                    // serverIds.push(res.serverId);
                    // localImagesIds.shift();
                    // uploadImages(localImagesIds);
                },
                fail: function (res) {
                    alert('上传失败,请重新上传!');
                }
            });
        }
    </script>

6.后台接受参数mediaId,保存图片至服务器

    @RequestMapping(value = "/savePicture", method= RequestMethod.POST)
    @ResponseBody
    public static Boolean savePicture(String mediaId) throws IOException{
        Boolean flag = true;
        String filename = WeiXinUtil.saveImageToDisk(mediaId);
        if(StringUtils.isEmpty(filename)){
            flag = false;
        }
        return flag;
    }

上述所有方法的工具类如下:包含所有的MD5加密,SHA1加密,微信签名,以及其他相关工具详见具体的方法注释

package com.huaqi.payment.util;
 
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.json.UTF8StreamJsonParser;
import com.huaqi.payment.domain.AccessToken;
import net.sf.json.JSONObject;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.util.StringUtils;
 
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
 
public class WeiXinUtil {
    //这里是微信鉴权认证的链接,详见微信公众号开发文档
    private static String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=APPSECRET&code=CODE&grant_type=authorization_code";
 
    //这里是微信公众号获取ticket的链接,详见微信公众号开发文档
    public final static String sign_ticket_create_url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
 
    public static AccessToken getAccessToken(String code) throws IOException{
        AccessToken token =  new AccessToken();
        String url = ACCESS_TOKEN_URL.replace("APPID",Constants.appID).replace("APPSECRET",Constants.secret).replace("CODE",code);
        JSONObject jsonObj = doGetStr(url);
        if(!StringUtils.isEmpty(jsonObj)){
            token.setToken(jsonObj.getString("access_token"));
            token.setExpiresIn(jsonObj.getInt("expires_in"));
            token.setOpenId(jsonObj.getString("openid"));
            token.setRefreshToken(jsonObj.getString("refresh_token"));
        }
        return token;
    }
 
    public static String  getJsapiTicket(String access_token) throws IOException{
        String url = sign_ticket_create_url.replace("ACCESS_TOKEN",access_token);
        JSONObject jsonObj = doGetStr(url);
        String ticket= jsonObj.getString("ticket");
        return ticket;
    }
 
    /**
     * 获取时间戳(秒)
     */
    public static String getTimestamp() {
        return String.valueOf(System.currentTimeMillis() / 1000);
    }
 
    /**
     * 获取当前时间 yyyyMMddHHmmss
     */
    public static String getCurrTime() {
        Date now = new Date();
        SimpleDateFormat outFormat = new SimpleDateFormat("yyyyMMddHHmmss");
        String s = outFormat.format(now);
        return s;
    }
 
    /**
     * 生成随机字符串
     */
    public static String getNonceStr() {
        String currTime = getCurrTime();
        String strTime = currTime.substring(8, currTime.length());
        String strRandom = buildRandom(4) + "";
        return strTime + strRandom;
    }
 
    /**
     * 取出一个指定长度大小的随机正整数.
     * @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 mediaId
     * @return 文件名
     */
    public static String saveImageToDisk(String mediaId)throws IOException{
        String filename = "";
        InputStream inputStream = getMediaStream(mediaId);
        byte[] data = new byte[1024];
        int len ;
        FileOutputStream fileOutputStream = null;
        try {
            //服务器存图路径
            String path = Constants.UPLOAD_PATH;
            filename = System.currentTimeMillis() + getNonceStr() + ".jpg";
            fileOutputStream = new FileOutputStream(path + File.separator+ filename);
            while ((len = inputStream.read(data)) != -1) {
                fileOutputStream.write(data, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fileOutputStream != null) {
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return filename;
    }
 
    /**
     * 获取微信Jsapi的accessToken
     * 这里获取的获取微信Jsapi的accessToken跟小程序以及其他的不一样
     */
    public static String getAccessToken() throws IOException{
        String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&&secret=APPSECRET";
        url = url.replace("APPID",Constants.appID).replace("APPSECRET",Constants.secret);
        JSONObject jsonObj = doGetStr(url);
        String accessToken = jsonObj.getString("access_token");
        return accessToken;
    }
 
    /**
     * 获取临时素材
     */
    private static InputStream getMediaStream(String mediaId)throws IOException {
        String url = "https://api.weixin.qq.com/cgi-bin/media/get";
        String access_token = getAccessToken();
        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;
    }
 
    public static JSONObject doGetStr(String url) throws IOException{
        HttpClient httpClient = new DefaultHttpClient();
        HttpGet  httpGet = new HttpGet(url);//HttpGet使用Get方式发送请求URL
        JSONObject jsonObj = null;
        HttpResponse  res = httpClient.execute(httpGet);//使用httpClient从Client执行httpGet的请求
        HttpEntity  entity = res.getEntity();//从HttpResponse中获取结果
        if(!StringUtils.isEmpty(entity)){
          String result =   EntityUtils.toString(entity,"utf-8");
            jsonObj = JSONObject.fromObject(result);//字符串类型转换为JSON对象
        }
        return jsonObj;
    }
 
    public static JSONObject doPostStr(String url,String outStr) throws IOException{
        HttpClient httpClient = new DefaultHttpClient();
        HttpPost httpPost = new HttpPost(url);//HttpGet使用Post方式发送请求URL
        JSONObject jsonObj = null;
        httpPost.setEntity(new StringEntity(outStr,"utf-8"));//使用setEntity方法,将传进来的参数放进请求
        HttpResponse  res = httpClient.execute(httpPost);
        HttpEntity  entity = res.getEntity();//从HttpResponse中获取结果
        if(!StringUtils.isEmpty(entity)){
            String result =   EntityUtils.toString(entity,"utf-8");
            jsonObj = JSONObject.fromObject(result);//字符串类型转换为JSON对象
        }
        return jsonObj;
    }
}

截止目前,调用微信js-sdk上传图片到服务器的主功能就结束了,可能还有一些小Bug,待笔者后续持续完善更新上述代码,想要整个流程完整代码的联系笔者电话(同微信):18629374628,共同交流

猜你喜欢

转载自blog.csdn.net/guobinhui/article/details/82897904