一、实现思路
1.使用微信JS-SDK工具包: <script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
2.点击a链接,触发click事件调用拍照接口【chooseImage】和上传图片接口【uploadImage】,拿到mediaId
3.调用后台接口拿到返回的图片流保存到服务器
官方文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1455784140&token=&lang=zh_CN
二、实现步骤
1.绑定域名
登录微信公众号平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。(如:xt.lab.cn)
如果没有测试公众号的话可以通过以下链接去注册一个,并配置JS接口安全域名
测试号管理:https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/index
在本页面还要配置一下微信认证网页授权,注意授权回调页面域名不带http和www,格式比如 baidu.com
2.在调用摄像头页面引入相关js文件
<script src="<%=request.getContextPath()%>/staticfile/js/jquery.min.js"></script>
<script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>
3.过congfig接口注入权限验证配置,并添加a链接(用来触发click事件)
<!-- a链接,触发调用摄像头 -->
<a href="javascript:" id="takePicture1" style="font-size: 26px">拍照</a>
<!-- 通过config接口注入权限验证配置 -->
<input type="hidden" id="appId" value="${appId }"/>
<input type="hidden" id="timestamp" value="${timestamp }"/>
<input type="hidden" id="nonceStr" value="${nonceStr }"/>
<input type="hidden" id="signature" value="${signature }"/>
<script>
$(function(){
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: $('#appId').val(), // 必填,公众号的唯一标识
timestamp: $('#timestamp').val(), // 必填,生成签名的时间戳
nonceStr: $('#nonceStr').val(), // 必填,生成签名的随机串
signature: $('#signature').val(),// 必填,签名,见附录1
jsApiList: ['chooseImage', 'uploadImage'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
});
});
</script>
4.JS-SDK使用权限签名算法,生成第三步中 config接口 所需参数
注意:java代码使用jfinal3.1框架,使用到的缓存插件、参数获取等语法不一致,自适应相应修改。
获取access_token(官方:有效期7200秒,开发者必须在自己的服务全局缓存access_token)、jsapi_ticket、签名sign
后台代码:
/**
* 初始化信息页面,获取config接口 所需参数
* @param request
* @param modelMap
* @return
*/
@RequestMapping("/configDetail")
public String assessList(HttpServletRequest request, ModelMap modelMap){
Map<String,String> params = getJsSdk(getJsTicket(),getUrl(request));
modelMap.put("timestamp",params.get("timestamp"));
modelMap.put("nonceStr",params.get("nonceStr"));
modelMap.put("signature",params.get("signature"));
modelMap.put("appId", WgjConstants.WX_APPID);
return "/wx/detail.ftl";
}
/**
* 保存图片到服务器,返回图片地址
*/
@RequestMapping(value = "/getLicensePlate")
@ResponseBody
public Map getLicensePlate(HttpServletRequest request) throws IOException {
Map map = new HashMap<>();
String mediaId = request.getParameter("mediaId");
// 保存图片到服务器,返回图片地址
String fileUrl = saveImageToDisk(mediaId );
map.put("fileUrl",fileUrl);
return map;
}
/**
* 保存图片至服务器
* @param mediaId
* @return 文件名
*/
public String saveImageToDisk(String mediaId){
String filename = "";
InputStream inputStream = getMedia(mediaId);
byte[] data = new byte[1024];
int len = 0;
FileOutputStream fileOutputStream = null;
try {
//服务器存图路径
String path = PathKit.getWebRootPath() + "/vehicleupload/";
filename = System.currentTimeMillis() + WeixinSignUtil.getNonceStr() + ".jpg";
fileOutputStream = new FileOutputStream(path + 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;
}
/**
* 生成随机串
*/
public static String getNonceStr() {
String currT = getCurrTime();
String strT = currT.substring(8, currT.length());
String strRandom = buildRandom(4) + "";
return strT + strRandom;
}
/**
* 获取图片流
*/
private InputStream getMedia(String mediaId) {
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;
}
/**
* 获取token
*
* @return
*/
public String getAccessToken() {
Gson gson = new Gson();
HttpUtil http = new HttpUtil();
String token_url="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
String tokenParam = http.get(String.format(token_url,"你的APPID","你的SECRET"));
logger.info("重新请求token:" + tokenParam);
Map param = gson.fromJson(tokenParam, Map.class);
String val = param.get("access_token").toString();
logger.info("重新获取到的token:" + val);
return val;
}
/**
* 获取ticket
* @return
* @throws Exception
*/
public String getJsTicket() {
String ticket_url="https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi";
String jsTicket = http.get(String.format(ticket_url, getAccessToken()));
logger.info("重新请求jsTicket:" + jsTicket);
Map<String, Object> map = gson.fromJson(jsTicket, Map.class);
String val = map.get("ticket").toString();
logger.info("重新获取到的ticket:" + val);
return val;
}
/**
* 获取jssdk签名
*
* @param jsapiTicket ticket
* @param url 请求界面的完整url,请去掉url“#”后面的所有字符
* @return
*/
public Map<String, String> getJsSdk(String jsapiTicket, String url) {
Map<String, String> params = new HashMap<String, String>();
String nonceStr = createNonceStr();
String timestamp = createTimestamp();
String string1, signature = "";
// 注意这里参数名必须全部小写,且必须有序
string1 = String.format("jsapi_ticket=%s&noncestr=%s×tamp=%s&url=%s", jsapiTicket, nonceStr, timestamp, url);
try {
MessageDigest crypt = MessageDigest.getInstance("SHA-1");
crypt.reset();
crypt.update(string1.getBytes("UTF-8"));
signature = byteToHex(crypt.digest());
} catch (NoSuchAlgorithmException e) {
logger.error("NoSuchAlgorithmException", e.getMessage());
} catch (UnsupportedEncodingException e) {
logger.error("UnsupportedEncodingException", e.getMessage());
}
params.put("url", url);
params.put("jsapi_ticket", jsapiTicket);
params.put("nonceStr", nonceStr);
params.put("timestamp", timestamp);
params.put("signature", signature);
return params;
}
/**
* 生成签名的时间戳
*
* @return
*/
private static String createTimestamp() {
return Long.toString(System.currentTimeMillis() / 1000);
}
/**
* 生成签名的随机串
*
* @return
*/
private static String createNonceStr() {
return UUID.randomUUID().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;
}
5.调用摄像头,获取mediaid,拿到图片流
$("#takePicture1").on('click',function(){
wx.chooseImage({
count: 1, // 默认9
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
success: function (res) {
var localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
wx.uploadImage({
localId: localIds.toString(), // 需要上传的图片的本地ID,由chooseImage接口获得
isShowProgressTips: 1, // 默认为1,显示进度提示
success: function (res) {
var mediaId = res.serverId; // 返回图片的服务器端ID,即mediaId
var params="mediaId="+mediaId;
//将获取到的 mediaId 传入后台 getLicensePlate,保存图片到服务器,并返回图片地址
$.ajax({
type:'post',
dataType:'json',
data:params,
// url:'/wx/assess/getLicensePlate.json',
success:function (d) {
alert(b.fileUrl);
},error:function () {
}
})
},
fail: function (res) {
alertModal('上传图片失败,请重试')
}
});
}
});
});
在此代码就编写完成了。