步骤一:绑定域名
先登录微信公众平台进入“公众号设置”的“功能设置”里填写“JS接口安全域名”。
备注:登录后可在“开发者中心”查看对应的接口权限。
步骤二:引入JS文件
在需要调用JS接口的页面引入如下JS文件,(支持https):http://res.wx.qq.com/open/js/jweixin-1.4.0.js
如需进一步提升服务稳定性,当上述资源不可访问时,可改访问:http://res2.wx.qq.com/open/js/jweixin-1.4.0.js (支持https)。
步骤三:后台获取参数传到前端配置
获取微信基础access_token(该token与微信支付的网页特授access_token不同),根据该token获取jsapi_ticket,在用该ticket生成signature(关键),
//获取基础支持中access_token,access_token的有效期目前为2个小时,需定时刷新,必须在自己的服务全局缓存access_token
String param="grant_type=client_credential&appid="+ PayConfUtil.APP_ID+"&secret="+PayConfUtil.SECRET;
String Json= HttpUtil.sendGet(PayConfUtil.token_url,param);
JSONObject json= JSON.parseObject(Json);
String access_token=json.getString("access_token");
HttpSession session=request.getSession();
session.setAttribute("access_token",access_token);
//获取jsapi_ticket是公众号用于调用微信JS接口的临时票据有效期目前为2个小时,需定时刷新,必须在自己的服务全局缓存jsapi_ticket
String param2="access_token="+access_token+"&type=jsapi";
String Json2=HttpUtil.sendGet(PayConfUtil.getticket_url,param2);
JSONObject json2= JSON.parseObject(Json2);
String ticket=json2.getString("ticket");
//JS-SDK使用权限签名算法
String noncestr= WXPayUtil.generateNonceStr();
String timestamp=WXPayUtil.getCurrentTimestamp()+"";
String url="http://wechat.muentech.cn/wx/Camera/wxLoginFace";
String ASCII="jsapi_ticket="+ticket+"&noncestr="+noncestr+"×tamp="+timestamp+"&url="+url;
String signature=WXPayUtil.shaEncode(ASCII);
//返回前端数据
Map<String,String> paraMap=new HashMap<String, String>();
paraMap.put("appId", PayConfUtil.APP_ID);
paraMap.put("timestamp",timestamp);
paraMap.put("nonceStr",noncestr);
paraMap.put("signature",signature);
步骤四:通过config接口注入权限验证配置
所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用
wx.config({
debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: appId, // 必填,公众号的唯一标识
timestamp:timestamp , // 必填,生成签名的时间戳
nonceStr: nonceStr, // 必填,生成签名的随机串
signature:signature ,// 必填,签名
jsApiList: ['checkJsApi','chooseImage','previewImage','uploadImage','downloadImage','getLocalImgData'] // 必填,需要使用的JS接口列表
});
步骤五:调用微信图像接口,将图片上传到微信服务器
var images = {
localId: null,
serverId: null
};
$("#imghead").click(function(){
wx.chooseImage({
count: 1, // 默认9
sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
sourceType: ['camera','album'], // 可以指定来源是相册还是相机,默认二者都有
success: function (res) {
images.localId = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
var img=document.getElementById("imghead");
img.src=images.localId[0];
images.serverId=null;
wx.uploadImage({
localId: images.localId[0], // 需要上传的图片的本地ID,由chooseImage接口获得
isShowProgressTips: 1, // 默认为1,显示进度提示
success: function (res) {
images.serverId = res.serverId; // 返回图片的服务器端ID
$.ajax({
url: "/wx/sendImg",
type: "POST",
data: {media_id: images.serverId},
success: function (data) {
if(data=="SUCCESS"){
alert("修改头像成功")
}
},
error:function () {
alert("error")
}
})
}
})
}
});
});
步骤六:从微信服务器获取图片网络流,发送网络流到阿里oss
@RequestMapping(value = "/sendImg",method = RequestMethod.POST)
@ResponseBody
public String sendImg(String media_id){
OSSClient ossClient = AliyunOssClientUtil.getOSSClient();
HttpSession session=request.getSession();
String access_token=(String) session.getAttribute("access_token");
System.out.println("access_token="+access_token);
System.out.println("media_id="+media_id);
//拼接请求地址
String requestUrl=PayConfUtil.media_url+"?access_token="+access_token+"&media_id="+media_id;
try {
//上传网络流
long time=System.currentTimeMillis();
URL url=new URL(requestUrl);
InputStream inputStream=url.openStream();
//获取报文长度
HttpURLConnection connection=(HttpURLConnection) url.openConnection();
connection.setDoInput(true);
connection.setRequestMethod("GET");
connection.setConnectTimeout(30000);
connection.setReadTimeout(30000);
String fileExt=connection.getHeaderField("Content-Type").substring(connection.getHeaderField("Content-Type").lastIndexOf("/")+1);
long ContentLength=connection.getContentLength();
String fileName="touxiang"+time+"."+fileExt;
try {
AliyunOssClientUtil.uploadFileOSS(inputStream,fileName,ossClient,ContentLength);
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
String pictureUrl = "https://" + OSSClientConstants.BACKET_NAME + "." + OSSClientConstants.ENDPOINT
+ File.separator + OSSClientConstants.FOLDER + fileName;
System.out.println(pictureUrl);
connection.disconnect();
return "SUCCESS";
}catch (Exception e){
e.printStackTrace();
System.out.println("下载图片失败");
return "FALL";
}
}
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.ObjectMetadata;
import java.io.IOException;
import java.io.InputStream;
import lombok.extern.slf4j.Slf4j;
import org.apache.log4j.Logger;
/**
* @author jack
*/
public class AliyunOssClientUtil {
// 阿里云API的内或外网域名
private static String ENDPOINT;
// 阿里云API的密钥Access Key ID
private static String ACCESS_KEY_ID;
// 阿里云API的密钥Access Key Secret
private static String ACCESS_KEY_SECRET;
// 阿里云API的bucket名称
private static String BACKET_NAME;
// 阿里云API的文件夹名称
private static String FOLDER;
// 阿里云API的文件夹名称
// 初始化属性
static {
ENDPOINT = OSSClientConstants.ENDPOINT;
ACCESS_KEY_ID = OSSClientConstants.ACCESS_KEY_ID;
ACCESS_KEY_SECRET = OSSClientConstants.ACCESS_KEY_SECRET;
BACKET_NAME = OSSClientConstants.BACKET_NAME;
FOLDER = OSSClientConstants.FOLDER;
}
private static Logger logger = Logger.getLogger(AliyunOssClientUtil.class);
public static OSSClient getOSSClient() {
return new OSSClient(ENDPOINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
}
private static String getContentType(String fileName) {
// 文件的后缀名
String fileExtension = fileName.substring(fileName.lastIndexOf("."));
if (".bmp".equalsIgnoreCase(fileExtension)) {
return "image/bmp";
}
if (".gif".equalsIgnoreCase(fileExtension)) {
return "image/gif";
}
if ( ".jpg".equalsIgnoreCase(fileExtension)
|| ".png".equalsIgnoreCase(fileExtension)) {
return "image/jpeg";
}
if (".jpeg".equalsIgnoreCase(fileExtension) ){
return "image/jpg";
}
if (".html".equalsIgnoreCase(fileExtension)) {
return "text/html";
}
if (".txt".equalsIgnoreCase(fileExtension)) {
return "text/plain";
}
if (".vsd".equalsIgnoreCase(fileExtension)) {
return "application/vnd.visio";
}
if (".ppt".equalsIgnoreCase(fileExtension) || "pptx".equalsIgnoreCase(fileExtension)) {
return "application/vnd.ms-powerpoint";
}
if (".doc".equalsIgnoreCase(fileExtension) || "docx".equalsIgnoreCase(fileExtension)) {
return "application/msword";
}
if (".xml".equalsIgnoreCase(fileExtension)) {
return "text/xml";
}
// 默认返回类型
return "image/jpeg";
}
/**
* 上传到OSS服务器 如果同名文件会覆盖服务器上的
*
* @param instream 文件流
* @param fileName 文件名称 包括后缀名
* @return 出错返回"" ,唯一MD5数字签名
*/
public static void uploadFileOSS(InputStream instream, String fileName, OSSClient ossClient,Long ContentLength) {
try {
// 创建上传Object的Metadata
ObjectMetadata objectMetadata = new ObjectMetadata();
objectMetadata.setContentLength(ContentLength);
objectMetadata.setCacheControl("no-cache");
objectMetadata.setHeader("Pragma", "no-cache");
objectMetadata.setContentType(getContentType(fileName.substring(fileName.lastIndexOf("."))));
objectMetadata.setContentDisposition("inline;filename=" + fileName);
// 上传文件
ossClient.putObject(BACKET_NAME, FOLDER + fileName, instream, objectMetadata);
ossClient.shutdown();
} catch (Exception e) {
log.error(e.getMessage(), e);
} finally {
try {
if (instream != null) {
instream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
本次学习的地方
1.inStream.available()读取文件流长度不不完整,这个方法从本地文件读取数据时一般不会出现问题,但是通过网路传输就会出现图片传输不完整的情况,因为网络通讯是间断性的一串字节往往分几批进行发送。本地程序调用available()方法有时得到0,这可能是对方还没有响应,也可能是对方已经响应了,但是数据还没有送达本地。
2.微信服务器返回的图片为jpeg格式,需将该图片转为jpg格式才可在浏览器中预览,否则一直是下载(没搞懂)
3.openStream和openConnection的区别:
openStream()方法的实现也是调用了 openConnection生成一个 URLConnection 对象,然后再通过这个对象调用的 getInputStream()方法的
openStream 是取 stream 的内容,而 openConnection 可以得到一些内容之外的东西,比如 modification date 和 http header
openStream就是把openConnection和getInputStream连起来调用了
微信js-sdk说明文档:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html
阿里oss开发文档:https://www.alibabacloud.com/help/zh/doc-detail/31883.htm