天天看點

JAVA實作微信公衆号調用攝像頭拍照和打開本地相冊上傳圖檔至伺服器

JAVA實作微信公衆号調用攝像頭拍照和打開本地相冊上傳圖檔至伺服器

一、主體實作流程

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

JAVA實作微信公衆号調用攝像頭拍照和打開本地相冊上傳圖檔至伺服器

在本頁面還要配置一下微信認證網頁授權,注意授權回調頁面域名不帶http和www,格式比如 42du.net

JAVA實作微信公衆号調用攝像頭拍照和打開本地相冊上傳圖檔至伺服器

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,共同交流