一、主体实现流程
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,共同交流