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
在本頁面還要配置一下微信認證網頁授權,注意授權回調頁面域名不帶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,共同交流