前段时间在做一个微信的项目,遇到了一个上传图片的问题,花了一下午,解决了这个问题,然后把总结出来的代码,分享了出来。
最近又有一个图片+语音的功能, 更是蛋疼, 本次采用的不是File文件上传,然后转码(Base64)的形式解决的,而是使用微信的开放Js-SDK实现的。
我们.net苦逼的很,微信官方的Demo上,有php、java、nodejs以及python的示例代码,就是没有我们.NET的,不知道大神们是不是都藏私了,现在大的不出来,做小的出来顶!d=====( ̄▽ ̄*)b
(开句玩笑话,别当真,鄙人菜鸟一枚)
闲话不聊, 马上进入正题
首先,我们看看官方给的 Js-SDK https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115&token=&lang=zh_CN
…… 文档相信大家已经不陌生了,如何配置才是问题, 接下来,给大家详解 微信Js-SDK的调用(顺带给大家介绍下使用,老手可以直接跳过前戏~ 一、二、三、四)
第一步: 绑定域名
域名是指安全回调的域名 , 首先打开属于你的公众号,
点开 公众号设置 - 功能设置 - 设置安全回调域名
如图:
设置好安全回调的域名以后, 下面进入第二步
第二步: 引入JS-SDK文件
官方已经给出了地址, 我们直接在对应的页面中 引用这个JS即可, 这个JS里面的东西, 有微信已经封装好的交互(深究的话,鄙人暂时无能为力),直接引入即可
<script src="http://res.wx.qq.com/open/js/jweixin-1.0.0.js"></script> 类似这样的
第三步: 通过config接口注入权限验证配置
从这个时候开始, 我们就需要配置调用 JS-SDK里面的方法了;
所有需要使用JS-SDK的页面必须先注入配置信息,否则将无法调用(同一个url仅需调用一次,对于变化url的SPA的web app可在每次url变化时进行调用,目前Android微信客户端不支持pushState的H5新特性,所以使用pushState来实现web app的页面会导致签名失败,此问题会在Android6.2中修复)。
wx.config({
debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
appId: ‘‘, // 必填,公众号的唯一标识
timestamp: , // 必填,生成签名的时间戳
nonceStr: ‘‘, // 必填,生成签名的随机串
signature: ‘‘,// 必填,签名,见附录1
jsApiList: [] // 必填,需要使用的JS接口列表, 可参考官方的目录,添加以后,可以使用该接口
});
下面是我在MVC里配置的代码附录
控制器端: var appId = WeixinConfig.AppID; var nonceStr = Util.CreateNonce_str(); var timestamp = Util.CreateTimestamp(); var domain = System.Configuration.ConfigurationManager.AppSettings["Domain"]; var url = domain + Request.Url.PathAndQuery; var jsTickect = WeixinConfig.TokenHelper.GetJSTickect(); var string1 = ""; var signature = JSAPI.GetSignature(jsTickect, nonceStr, timestamp, url, out string1); ViewBag.JSAPI = new JSSDKModel { appId = appId, nonceStr = nonceStr, signature = signature, timestamp = timestamp, string1 = string1, };
@VIew页面端: //配置微信 wx.config({ debug: false, appId: ‘@ViewBag.JSAPI.appId‘, // 必填,公众号的唯一标识 timestamp: @ViewBag.JSAPI.timestamp, // 必填,生成签名的时间戳 nonceStr: ‘@ViewBag.JSAPI.nonceStr‘, // 必填,生成签名的随机串 signature: ‘@ViewBag.JSAPI.signature‘,// 必填,签名,见附录1 jsApiList: [ ‘chooseImage‘, ‘previewImage‘, ‘uploadImage‘, ‘downloadImage‘, ‘checkJsApi‘, ‘startRecord‘, ‘stopRecord‘, ‘onVoiceRecordEnd‘, ‘playVoice‘, ‘pauseVoice‘, ‘stopVoice‘, ‘onVoicePlayEnd‘, ‘uploadVoice‘, ‘downloadVoice‘, ‘getNetworkType‘ ] });
配置完成后,我们进入第四步。
第四步: 接口验证
官方给的验证方法有 Ready 成功验证 和 Error 失败验证,下面我们看看代码
wx.ready(function(){ // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。 }); 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为调用的接口名
调用失败时:其值为具体错误信息
了解了以上信息以后,接下来就可以进入我们今天主要探讨的一个问题了——微信的图片接口和语音接口
我们先看图片接口,然后一一实验
图像接口 拍照或从手机相册中选图接口 wx.chooseImage({ count: 1, // 默认9 sizeType: [‘original‘, ‘compressed‘], // 可以指定是原图还是压缩图,默认二者都有 sourceType: [‘album‘, ‘camera‘], // 可以指定来源是相册还是相机,默认二者都有 success: function (res) { var localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片 } }); 预览图片接口 wx.previewImage({ current: ‘‘, // 当前显示图片的http链接 urls: [] // 需要预览的图片http链接列表 }); 上传图片接口 wx.uploadImage({ localId: ‘‘, // 需要上传的图片的本地ID,由chooseImage接口获得 isShowProgressTips: 1, // 默认为1,显示进度提示 success: function (res) { var serverId = res.serverId; // 返回图片的服务器端ID } }); 备注:上传图片有效期3天,可用微信多媒体接口下载图片到自己的服务器,此处获得的 serverId 即 media_id。 下载图片接口 wx.downloadImage({ serverId: ‘‘, // 需要下载的图片的服务器端ID,由uploadImage接口获得 isShowProgressTips: 1, // 默认为1,显示进度提示 success: function (res) { var localId = res.localId; // 返回图片下载后的本地ID } });
关于图片这边,微信官方提供了四大接口,分别是选择图片的接口, 这个选择类似于微信的图片选择,其实就是JS和微信的交互体验,
选择一个一个接口解释
选择图片接口
chooseImage
:在选择好图片以后,图片有一个localId返回,这个localId是微信缓存本地的一个ID,可以直接使用这个localId,进行图片显示,
例如: $("#img").append(‘<img src="‘+localId+‘" style="width:50px; height:50px;"/> <br />‘); 这样的一句话,会在img标签里面,添加一张刚刚你选择的图片
预览图片接口
previewImage
:我们注意到,这个预览图片的接口,有两个参数, 一个是 current ,一个 urls , 这两个参数是什么意思呢? 首先, 不管是current 还是 urls , 里面的属性值, 都是http:// 的 需要预览的图片的地址, current 表示在调用这个接口时
会显示在第一页的图片, 而urls是表示,显示的图片集合 , 例如 :
var imgList = [ ‘http://h.hiphotos.baidu.com/image/pic/item/4bed2e738bd4b31c70a6b76782d6277f9f2ff850.jpg‘, ]; wx.previewImage({ current: imgList[0], urls: imgList });
这样,当点击的事件触发到这个方法后,会调用这个预览接口,实现微信端看到的,点小图, 看到大图的效果
上传图片接口 uploadImage
上传图片这个接口,很多人有这样的疑问(包括我第一次也是): 这个上传的图片在哪? 上传到哪儿了去了? 我们怎么才能用这个图片?
接下来,我感同身受的,一个一个解决这个问题。
疑: 上传的图片在哪里? - 答 : 我们注意到,之前在调用选择图片的接口的时候,有一个localId , 我解释过,是微信端缓存本地的一个图片编号,那么,我们联想一下, 这个localId ,是否就是选择得到的localId了, 没有错, 就是这个localId, 那也就是说,
我们想要使用上传接口, 首先, 我们得选择图片,才能进行相应的上传操作。
疑: 上传到哪儿去了? - 答: 当存在这个localId的时候,接口会找到相对应的图片上,然后把该图片,通过另一个接口 : https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE
上传至微信服务器的临时素材里面,当然,这个临时素材只有三天,并且有空间限制, 具体参考微信公众号里面的接口权限一栏 。
疑: 我们怎么才能用这个图片? - 答: 当调用图片上传接口成功, 会有一个返回值返回给我们 ,类型是这样的- {"type":"TYPE","media_id":"MEDIA_ID","created_at":123456789} ,
我们需要注意到的有用的信息 就是这个 media_id ,这个media_id 表示我们所上传的素材, 在素材库里的位置, 当我们想要使用它的时候, 只需要把这个 media_id 还有 token 请求到素材请求的地址, 就可以得到使用这个素材了。
下载图片接口
downloadImage
其实在上一个接口里面,稍稍已经提到过这个接口的使用, 没有错, 这个接口就是为了下载临时的素材,然后返回一个localId, 此时的localId, 是一个全新的, 代表的是下载到的图片的localId, 使用的方法也还是很简单, 通过一个 Src 属性即可使用
讲到这里, 不得不停一下, 我们似乎忽略了些什么东西了,这些图片只能用三天,那三天后怎么办? 还有啊, 这个素材库有大小的限制, 不够了怎么办?
怎么办? 问我好咯, 我来分享一个办法—— 那就是转存到我们的服务器上, 微信已经替我们压缩了,我们下载下来, 然后转一下,送到我们自己的服务器上,留个地址,在我们的数据库存储一下,
一张可以持久高清无码图, 就这样被我们收入“囊中”。
下面继续贴代码, 看不懂的私聊,
<a href="javascript:void(0);" onclick="javascript:chooseImage();">选择图片</a> <a href="javascript:void(0);" onclick="javascript:uploadImage();">图片上传</a> <a href="javascript:void(0);" onclick="javascript:downloadImage();">图片下载</a> <a href="javascript:void(0);" onclick="javascript:previewImage();">图片预览</a> <div id="img">选择的图片</div> <div id="img2"> 下载的图片</div> <script type="text/javascript"> wx.config({ debug: false, appId: ‘@ViewBag.JSAPI.appId‘, // 必填,公众号的唯一标识 timestamp: @ViewBag.JSAPI.timestamp, // 必填,生成签名的时间戳 nonceStr: ‘@ViewBag.JSAPI.nonceStr‘, // 必填,生成签名的随机串 signature: ‘@ViewBag.JSAPI.signature‘,// 必填,签名,见附录1 jsApiList: [ ‘chooseImage‘, ‘previewImage‘, ‘uploadImage‘, ‘downloadImage‘, ‘checkJsApi‘, ‘startRecord‘, ‘stopRecord‘, ‘onVoiceRecordEnd‘, ‘playVoice‘, ‘pauseVoice‘, ‘stopVoice‘, ‘onVoicePlayEnd‘, ‘uploadVoice‘, ‘downloadVoice‘, ‘getNetworkType‘ ] }); wx.ready(function () { //返回网络类型 wx.getNetworkType({ success: function (res) { var networkType = res.networkType; // 返回网络类型2g,3g,4g,wifi jQuery(function() { $(‘#networkType‘).html(networkType); }); } }); }); // 5.1 拍照、本地选图 var images = { localId: [], serverId: [] }; function chooseImage() { wx.chooseImage({ success: function (res) { images.localId = res.localIds; jQuery(function(){ $.each(res.localIds, function(i, n){ $("#img").append(‘<img src="‘+n+‘" style="width:50px; height:50px;"/> <br />‘); }); }); } }); } // 5.2 图片预览 function previewImage() { var imgList = [ ‘http://h.hiphotos.baidu.com/image/pic/item/4bed2e738bd4b31c70a6b76782d6277f9f2ff850.jpg‘, ]; wx.previewImage({ current: imgList[0], urls: imgList }); } // 5.3 上传图片 var i = 0, length =0; function uploadImage(){ if (images.localId.length == 0) { alert(‘请先使用选择图片按钮‘); return; } length= images.localId.length; upload(); } function upload() { wx.uploadImage({ localId: images.localId[i], success: function (res) { i++; //alert(‘已上传:‘ + i + ‘/‘ + length); images.serverId.push(res.serverId); $("#img2").append(‘<img src="‘+res.localId+‘" style="width:50px; height:50px;"/ /> <br />‘); downloadImage(res.serverId); if (i < length) { upload(); } }, fail: function (res) { alert(JSON.stringify(res)); } }); } //下载图片 function downloadImage(serverId) { $.get("@Url.Action("WeixinDownloadImage","Home")",{mediaId:serverId},function(data){ alert(data); },"json") } wx.error(function (res) { alert(res.errMsg); }); </script>
注意到,我有定义一个全局变量, 可以拓展到多图上传, 还有一个递归的上传。 我先回去了,下班了,回家烧饭吃。
服务器端的,和语音的,下次再聊。