微信公众号网页开发之拍照、上传本地图片

2022-09-02 15:24:06 浏览数 (1)

大家好,又见面了,我是你们的朋友全栈君。

微信网页开发

JS-SDK说明文档:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html#0

绑定域名

登录微信公众平台进入“公众号设置”->“功能设置”,填写“JS接口安全域名”;设置JS接口安全域名后,公众号开发者可在该域名下调用微信开放的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)。

通过config接口注入权限验证配置

所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用,目前Android微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题会在Android6.2中修复)。

代码语言:javascript复制
wx.config({
  debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
  appId: '', // 必填,公众号的唯一标识
  timestamp: , // 必填,生成签名的时间戳
  nonceStr: '', // 必填,生成签名的随机串
  signature: '',// 必填,签名
  jsApiList: [] // 必填,需要使用的JS接口列表
});

通过ready接口处理成功验证

代码语言:javascript复制
wx.ready(function(){
  // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
});

通过error接口处理失败验证

代码语言:javascript复制
wx.error(function(res){
  // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
});

接口调用说明

所有接口通过wx对象(也可使用jWeixin对象)来调用,参数是一个对象,除了每个接口本身需要传的参数之外,还有以下通用参数:

  1. success:接口调用成功时执行的回调函数。
  2. fail:接口调用失败时执行的回调函数。
  3. complete:接口调用完成时执行的回调函数,无论成功或失败都会执行。
  4. cancel:用户点击取消时的回调函数,仅部分有用户取消操作的api才会用到。
  5. trigger: 监听Menu中的按钮点击时触发的方法,该方法仅支持Menu中的相关接口。

备注:不要尝试在trigger中使用ajax异步请求修改本次分享的内容,因为客户端分享操作是一个同步操作,这时候使用ajax的回包会还没有返回。

以上几个函数都带有一个参数,类型为对象,其中除了每个接口本身返回的数据之外,还有一个通用属性errMsg,其值格式如下:

调用成功时:”xxx:ok” ,其中xxx为调用的接口名

用户取消时:”xxx:cancel”,其中xxx为调用的接口名

调用失败时:其值为具体错误信息

基础接口

判断当前客户端版本是否支持指定JS接口

代码语言:javascript复制
wx.checkJsApi({
  jsApiList: ['chooseImage'], // 需要检测的JS接口列表,所有JS接口列表见附录2,
  success: function(res) {
  // 以键值对的形式返回,可用的api值true,不可用为false
  // 如:{"checkResult":{"chooseImage":true},"errMsg":"checkJsApi:ok"}
  }
});

示例

html及js调用微信jsapi接口

代码语言:javascript复制
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>测试上传图片</title>

<script src="./js/jquery-1.12.4.min.js"></script>
<!--需要调用摄像头-->
<script src="http://res.wx.qq.com/open/js/jweixin-1.2.0.js"></script>

<script type="text/javascript">
	function chooseImg(){
		var data = {
			    "reqHead": {
			        "token": "1dda44c1afc77e875fdfd1nd9fdc3f",
			        "transCode": "VYK00505",
			        "openId": "openId",
			        "transTime": "2018-02-12 15:28:02"
			    },
			    "reqBody": {
			        "url": location.href.split('#')[0]//当前页面url
			    }
			};
		$.ajax({
	        url:"http://***.***.com/examAction!examBank.action",//获取appId、时间戳、随机字符串、签名等参数接口
	        dataType:"json",
	        type:"post",
	        contentType : "application/json;charsetset=UTF-8",//必须
	        data : JSON.stringify(data),//必须
	        success:function(res){
				wx.config({
				       debug: true,//开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
				       appId:res.resBody.appId, // 必填,公众号的唯一标识
				       timestamp: res.resBody.timestamp, // 必填,生成签名的时间戳
				       nonceStr: res.resBody.nonceStr, // 必填,生成签名的随机串
				       signature: res.resBody.signature,// 必填,签名,见附录1
				       jsApiList: ['chooseImage','previewImage','uploadImage','downloadImage']// 必填
				    });
		       	wx.ready(function(){
					//检查config是否正确
					wx.checkJsApi({
						jsApiList: [
						    'chooseImage',//拍照或从手机相册中选图接口
						    'previewImage',//预览图片接口
						    'uploadImage',//上传图片接口
						    'downloadImage'//下载图片接口
						],
						success: function (res) {//检查结果成功
						    //alert(JSON.stringify(res));
						    //alert(JSON.stringify(res.checkResult.getLocation));
						    if (res.checkResult.getLocation == false) {
						        alert('你的微信版本太低,不支持微信JS接口,请升级到最新的微信版本!');
						        return;
						    }else{
						    	var localIds=[];
						        var serverIds=[];
						      	wx.chooseImage({
						      		count: 6, // 微信默认9
							          sizeType: ['original', 'compressed'],// 可以指定是原图还是压缩图,默认二者都有
							          sourceType: ['album', 'camera'],// 可以指定来源是相册还是相机,默认二者都有
							          success: function (res) {
							        	  console.log(res);
							              serverIds=[];
								          medil_id="";
								          var imgUrl= "";
								          localIds = res.localIds;// 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
								          //console.log('已选择 '   res.localIds.length   ' 张图片');
								          if(res.localIds.length == 0) {
								          	console.log('请先使用微信的 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="480px" style="margin-right: 10px;" src="' localIds[i] '"/></div>');
							              }
							          },
							          fail:function(res){
							        	    console.log("失败的原因" res);
							          }
						        })
						    }
						}
				    });

			});
	        },
            error:function(){
         	   console.log("上传失败");
            }
	   })
	}

	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
	            //调用方法savePicture,将获取到的 mediaId 传入后台 
	            /* $.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) {
	        	console.log('上传失败,请重新上传!');
	        }
	    });
	}
</script>

</head>
<body>
<div align="center">
   <div onclick="chooseImg()" style=" background-color: aquamarine;width: 500px;height: 100px; line-height: 90px;">点击上传图片</div>
   <div class="img_container"></div>
</div>

</body>
</html>

后台获取ACCESS_TOKEN和JSAPI_TICKET

JS-SDK使用权限签名算法 jsapi_ticket 生成签名之前必须先了解一下jsapi_ticket,jsapi_ticket是公众号用于调用微信JS接口的临时票据。正常情况下,jsapi_ticket的有效期为7200秒,通过access_token来获取。由于获取jsapi_ticket的api调用次数非常有限,频繁刷新jsapi_ticket会导致api调用受限,影响自身业务,开发者必须在自己的服务全局缓存jsapi_ticket 。

  1. 参考以下文档获取access_token(有效期7200秒,开发者必须在自己的服务全局缓存access_token):https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
  2. 用第一步拿到的access_token 采用http GET方式请求获得jsapi_ticket(有效期7200秒,开发者必须在自己的服务全局缓存jsapi_ticket):https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi

成功返回如下JSON: { "errcode":0, "errmsg":"ok", "ticket":"bxLdikRXVbTPdHSM05e5u5sUoXNKd8-41ZO3MhKoyN5OfkWITDGgnr2fwJ0m9E8NYzWKVZvdVtaUgWvsdshFKA", "expires_in":7200 } 获得jsapi_ticket之后,就可以生成JS-SDK权限验证的签名了。 签名算法 签名生成规则如下:参与签名的字段包括noncestr(随机字符串), 有效的jsapi_ticket, timestamp(时间戳), url(当前网页的URL,不包含#及其后面部分) 。对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1。这里需要注意的是所有参数名均为小写字符。对string1作sha1加密,字段名和字段值都采用原始值,不进行URL 转义。 即signature=sha1(string1)。 示例: noncestr=Wm3WZYTPz0wzccnW jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg timestamp=1414587457 url=http://mp.weixin.qq.com?params=value 步骤1. 对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)后,使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串string1: jsapi_ticket=sM4AOVdWfPE4DxkXGEs8VMCPGGVi4C3VM0P37wVUCFvkVAy_90u5h9nbSlYy3-Sl-HhTdfl2fzFy1AOcHKP7qg&noncestr=Wm3WZYTPz0wzccnW&timestamp=1414587457&url=http://mp.weixin.qq.com?params=value 步骤2. 对string1进行sha1签名,得到signature: 0f9de62fce790f9a083d5c99e95740ceb90c27ed 注意事项

  1. 签名用的noncestr(js中驼峰标准大写S)和timestamp必须与wx.config中的nonceStr和timestamp相同。
  2. 签名用的url必须是调用JS接口页面的完整URL(请在当前页面alert(location.href.split(‘#’)[0])确认),包括’http(s)://’部分,以及’?’后面的GET参数部分,但不包括’#’hash后面的部分。
  3. 出于安全考虑,开发者必须在服务器端实现签名的逻辑。
  4. 第三方切记要对获取的accesstoken以及jsapi_ticket进行缓存以确保不会触发频率限制。

后端java获取签名

代码语言:javascript复制
Map<String, String> result = new HashMap<String, String>();
//appId
result.put("appId", WX_APP_ID);
//时间戳
long timeStampSec = System.currentTimeMillis() / 1000;
String timestamp = String.format("0d", timeStampSec);
result.put("timestamp", timestamp);
//随机串
String nonceStr = getRandomStr(8);
result.put("nonceStr", nonceStr);
//签名
result.put("signature", getWXSignature(url,timestamp,nonceStr));

//1.获取access_token,格式:{"access_token":"ACCESS_TOKEN","expires_in":7200},参考地址:https://developers.weixin.qq.com/doc/offiaccount/Basic_Information/Get_access_token.html
private static String getWXaccessToken(){
	//获取access_token缓存到redis,设置缓存时间为7000秒
	JedisCache jedis = JedisCache.getInstance();
	jedis.setLocalAlias(JedisKeys.JEDIS_ALIAS1);
	if(!jedis.exists(JedisKeys.ACCESS_TOKEN)){
		String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" WX_APP_ID "&secret=" WX_SECRET;//获取access_token时grant_type参数填写client_credential
		String tokenResult = HttpUtil.httpsGet(url);
		if (StringUtils.isNotBlank(tokenResult)) {
			System.out.println("获取access_token请求结果:"   tokenResult);
			JSONObject tokenObj = JSON.parseObject(tokenResult);
			String accessToken = tokenObj.getString("access_token");
			System.out.println("获取到的access_token:"   accessToken);
			//缓存ticket值,设置有效时间
			jedis.set(JedisKeys.ACCESS_TOKEN, accessToken);
			jedis.expire(JedisKeys.ACCESS_TOKEN, 7000);
		}
	}
	return jedis.get(JedisKeys.ACCESS_TOKEN);
}
//2.使用access_token 采用http GET方式请求获得jsapi_ticket
private static String getWXJsapiTicket() {
	JedisCache jedis = JedisCache.getInstance();
	jedis.setLocalAlias(JedisKeys.JEDIS_ALIAS1);
	if(!jedis.exists(JedisKeys.JSAPI_TICKET)){
		//获取ticket的地址
		String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token="   getWXaccessToken()   "&type=jsapi";
		String resp = HttpUtil.httpsGet(url);
		if(StringUtils.isNotBlank(resp)){
			System.out.println("获取jsapi_ticket请求结果:"   resp);
			JSONObject resJson = JSONObject.parseObject(resp);
			//请求结果ticket值
			String ticket = resJson.getString("ticket");
			System.out.println("获取到的ticket:"   ticket);
			//缓存ticket值,设置有效时间
			jedis.set(JedisKeys.JSAPI_TICKET, ticket);
			jedis.expire(JedisKeys.JSAPI_TICKET, 7000);
		}
	}
	return jedis.get(JedisKeys.JSAPI_TICKET);
}
//3.生成签名
private static String getWXSignature(String url,String timestamp,String nonceStr) {
	//时间戳 timestamp
	//随机字符串 nonceStr
	//分享的网页的URL,不包含#及其后面部分
	String[] urls = url.split("#");
	String newUrl = urls[0];
	if(newUrl.indexOf("http") != 0){
		newUrl = "http://"   newUrl;
	}
	//对所有待签名参数按照字段名的ASCII 码从小到大排序(字典序)
	String[] signArr = new String[]{"url="   newUrl, "jsapi_ticket="   getWXJsapiTicket(), "noncestr="   nonceStr, "timestamp="   timestamp};
	Arrays.sort(signArr);
	String signStr = StringUtils.join(signArr, "&");
	//对signStr作sha1加密
	return DigestUtils.sha1Hex(signStr);
}
//获取随机8位字符串
public static String getRandomStr(int length) {
	String base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
	int randomNum;
	char randomChar;
	Random random = new Random();
	// StringBuffer类型的可以append增加字符
	StringBuffer str = new StringBuffer();

	for (int i = 0; i < length; i  ) {
		// 可生成[0,n)之间的整数,获得随机位置
		randomNum = random.nextInt(base.length());
		// 获得随机位置对应的字符
		randomChar = base.charAt(randomNum);
		// 组成一个随机字符串
		str.append(randomChar);
	}
	return str.toString();
}

http get请求

代码语言:javascript复制
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.params.HttpMethodParams;
import org.apache.commons.httpclient.HttpException;
import java.io.IOException;

public class HttpUtil {

	private HttpUtil(){}
	
	public static String httpsGet(String url){
		HttpClient client = new HttpClient();
		Protocol myhttps = new Protocol("https", new MySSLProtocolSocketFactory(), 443);
		Protocol.registerProtocol("https", myhttps);
		GetMethod getMethod = new GetMethod(url);
		getMethod.setFollowRedirects(true);
		getMethod.addRequestHeader("Content-Type","text/html;charset=UTF-8"); 
		HttpMethodParams params = new HttpMethodParams();
		params.setContentCharset("UTF-8");
		getMethod.setParams(params);
		try {
			client.executeMethod(getMethod);
			return getMethod.getResponseBodyAsString();
		} catch (HttpException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}

常见错误及解决方法

调用config 接口的时候传入参数 debug: true 可以开启debug模式,页面会alert出错误信息。以下为常见错误及解决方法:

  1. invalid url domain当前页面所在域名与使用的appid没有绑定,请确认正确填写绑定的域名,仅支持80(http)和443(https)两个端口,因此不需要填写端口号(一个appid可以绑定三个有效域名,见 ]目录1.1.1)。
  2. invalid signature签名错误。建议按如下顺序检查:
    1. 确认签名算法正确,可用http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign 页面工具进行校验。
    2. 确认config中nonceStr(js中驼峰标准大写S), timestamp与用以签名中的对应noncestr, timestamp一致。
    3. 确认url是页面完整的url(请在当前页面alert(location.href.split(‘#’)[0])确认),包括’http(s)://’部分,以及’?’后面的GET参数部分,但不包括’#’hash后面的部分。
    4. 确认 config 中的 appid 与用来获取 jsapi_ticket 的 appid 一致。
    5. 确保一定缓存access_token和jsapi_ticket。
    6. 确保你获取用来签名的url是动态获取的,动态页面可参见实例代码中php的实现方式。如果是html的静态页面在前端通过ajax将url传到后台签名,前端需要用js获取当前页面除去’#’hash部分的链接(可用location.href.split(‘#’)[0]获取,而且需要encodeURIComponent),因为页面一旦分享,微信客户端会在你的链接末尾加入其它参数,如果不是动态获取当前链接,将导致分享后的页面签名失败。
  3. the permission value is offline verifying这个错误是因为config没有正确执行,或者是调用的JSAPI没有传入config的jsApiList参数中。建议按如下顺序检查:
    1. 确认config正确通过。
    2. 如果是在页面加载好时就调用了JSAPI,则必须写在wx.ready的回调中。
    3. 确认config的jsApiList参数包含了这个JSAPI。

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/139523.html原文链接:https://javaforall.cn

0 人点赞