微信开发与代码的编写(二)
微信开发与代码的编写(二)
普通消息的接收和回复
微信公众平台消息管理接口介绍
要实现微信公众号的普通消息的接收和回复,我们需要先熟悉微信公众平台API中消息接口部分,点此进入,点击后将进入到【消息管理】部分,如下图所示:
对于普通消息的接收和回复我们只需要关注上图中的"接收消息——接收普通消息"和"发送消息——被动回复消息"
消息接收
先来说说接收消息, 当普通微信用户向公众账号发消息时,微信服务器会先接收到用户发送的消息,然后将用户消息按照指定的XML格式组装好数据,最后POST消息的XML数据包到开发者填写的URL上。
接收到的普通消息的消息类型目前有以下几种:
1 文本消息
2 图片消息
3 语音消息
4 视频消息
5 小视频消息
6 地理位置消息
7 链接消息
每一种消息类型都有其指定的XML数据格式,这7种消息的xml格式请到官方文档查看,有具体的格式定义和属性说明。格式很简单,基本共有属性包括ToUserName、FromUserName、CreateTime、MsgType、MsgId,并且每种类型有自己特殊的属性。
接收消息的过程其实就是获取post请求的这个xml,然后对这个xml进行分析的过程。post请求的入口还是之前提到的微信公众号接入的那个地址,整个公众号的所有请求都会走这个入口,只是接入时是get请求,其它情况下是post请求。
消息回复
微信服务器在将用户的消息发给公众号的开发者服务器地址后,会等待开发者服务器回复响应消息。微信服务器在五秒内收不到响应会断掉连接,并且重新发起请求,总共重试三次。
假如服务器无法保证在五秒内处理并回复,必须做出下述回复,这样微信服务器才不会对此作任何处理,并且不会发起重试(这种情况下,可以使用客服消息接口进行异步回复),否则,将出现严重的错误提示。详见下面说明:
1、(推荐方式)直接回复success2、直接回复空串(指字节长度为0的空字符串,而不是XML结构体中content字段的内容为空)
一旦遇到以下情况,微信都会在公众号会话中,向用户下发系统提示“该公众号暂时无法提供服务,请稍后再试”:
1、开发者在5秒内未回复任何内容2、开发者回复了异常数据,比如JSON数据等
另外,请注意,回复图片等多媒体消息时需要预先通过素材管理接口上传临时素材到微信服务器,可以使用素材管理中的临时素材,也可以使用永久素材。
消息回复目前支持回复文本、图片、图文、语音、视频、音乐,每一种类型的消息都有特定的XML数据格式。这几种回复消息的xml数据格式请参考官方文档,有具体的格式定义和属性说明。格式很简单,基本共有属性包括ToUserName、FromUserName、CreateTime、MsgType,并且每种类型有自己特殊的属性。
微信公众号的普通消息的接收和回复
接收消息
接收消息和被动回复消息这两个动作是不分家的,这本来就是一个交互场景,一般情况就是公众号通过分析接收到的消息,会给出对应的回复。
之前说过了,接收消息的过程其实就是获取微信服务器通过post请求的发送给我们公众号服务器的xml数据,然后我们的公众号服务器再对这个xml进行解析处理的过程。为了方便解析XML数据,我们借助于dom4j,dom4j是一个十分优秀的JavaXML API,具有性能优异、功能强大和极其易使用的特点,是用来读写XML文件的。针对微信服务器发来的xml请求数据,我们写一个parseXml方法来处理,parseXml方法的代码如下:
/*** 解析微信发来的请求(XML)** @param request 封装了请求信息的HttpServletRequest对象* @return map 解析结果* @throws Exception*/public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {// 将解析结果存储在HashMap中Map<String, String> map = new HashMap<String, String>();// 从request中取得输入流InputStream inputStream = request.getInputStream();// 读取输入流SAXReader reader = new SAXReader();Document document = reader.read(inputStream);// 得到xml根元素Element root = document.getRootElement();// 得到根元素的所有子节点List<Element> elementList = root.elements();// 遍历所有子节点for (Element e : elementList) {System.out.println(e.getName() + "|" + e.getText());map.put(e.getName(), e.getText());}// 释放资源inputStream.close();inputStream = null;return map;}
然后在处理微信请求的入口servlet的doPost方法中调用parseXml方法即可,调用代码如下:
/*** 处理微信服务器发来的消息*/protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {// TODO 接收、处理、响应由微信服务器转发的用户发送给公众帐号的消息// 将请求、响应的编码均设置为UTF-8(防止中文乱码)request.setCharacterEncoding("UTF-8");response.setCharacterEncoding("UTF-8");System.out.println("请求进入");String responseMessage;try {//解析微信发来的请求,将解析后的结果封装成Map返回Map<String,String> map = MessageHandlerUtil.parseXml(request);System.out.println("开始构造响应消息");responseMessage = MessageHandlerUtil.buildResponseMessage(map);System.out.println(responseMessage);if(responseMessage.equals("")){responseMessage ="未正确响应";}} catch (Exception e) {e.printStackTrace();System.out.println("发生异常:"+ e.getMessage());responseMessage ="未正确响应";}//发送响应消息response.getWriter().println(responseMessage);}
这样我们就完成了消息的接收,消息接收之后,我们就要根据消息类型进行响应了,写一个根据消息类型构造返回消息的方法,代码如下:
/*** 根据消息类型构造返回消息* @param map 封装了解析结果的Map* @return responseMessage(响应消息)*/public static String buildResponseMessage(Map map) {//响应消息String responseMessage = "";//得到消息类型String msgType = map.get("MsgType").toString();System.out.println("MsgType:" + msgType);//消息类型MessageType messageEnumType = MessageType.valueOf(MessageType.class, msgType.toUpperCase());switch (messageEnumType) {case TEXT://处理文本消息responseMessage = handleTextMessage(map);break;case IMAGE://处理图片消息responseMessage = handleImageMessage(map);break;case VOICE://处理语音消息responseMessage = handleVoiceMessage(map);break;case VIDEO://处理视频消息responseMessage = handleVideoMessage(map);break;case SHORTVIDEO://处理小视频消息responseMessage = handleSmallVideoMessage(map);break;case LOCATION://处理位置消息responseMessage = handleLocationMessage(map);break;case LINK://处理链接消息responseMessage = handleLinkMessage(map);break;case EVENT://处理事件消息,用户在关注与取消关注公众号时,微信会向我们的公众号服务器发送事件消息,开发者接收到事件消息后就可以给用户下发欢迎消息responseMessage = handleEventMessage(map);default:break;}//返回响应消息return responseMessage;}
这样我们就完成了根据消息类型进行响应了,在处理微信请求的入口servlet的doPost方法中调用buildResponseMessage方法即可,doPost方法的完整代码在上面已经贴出来了.buildResponseMessage方法中使用到了一个MessageType类,这是一个消息类型枚举类,MessageType类的代码如下:
/*** 接收到的消息类型*/public enum MessageType {TEXT,//文本消息IMAGE,//图片消息VOICE,//语音消息VIDEO,//视频消息SHORTVIDEO,//小视频消息LOCATION,//地理位置消息LINK,//链接消息EVENT//事件消息
}
回复消息
下面我基于这样一个业务场景来演示构造回复的消息,接收到文本消息"文本",回复文本消息;接收到“图片”,回复图片消息;接收到“语音”,回复语音消息;接收到“视频”,回复视频消息;接收到“音乐”,回复音乐消息;接收到“图文”,回复图文消息。下面具体说明各种消息的构建,只贴出核心代码,一些辅助代码类请自行下载项目代码参考.
回复文本消息
接收的文本消息的XML数据格式如下:
<xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>1348831860</CreateTime> <MsgType><![CDATA[text]]></MsgType><Content><![CDATA[this is a test]]></Content><MsgId>1234567890123456</MsgId></xml>
回复的文本消息的XML数据格式如下:
<xml><ToUserName><![CDATA[发消息的人,即订阅者]]></ToUserName><FromUserName><![CDATA[微信公众号本身]]></FromUserName><CreateTime>消息创建时间(整形)</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[消息内容]]></Content></xml>
其中接收消息格式中的ToUserName便是回复消息的FromUserName,接收消息格式中的FromUserName便是回复消息的ToUserName。CreateTime为消息发送的时间戳。MsgType为消息类型,文本为text。Content为消息内容。具体每一种类型消息的回复,就是构造此种类型的xml格式内容,格式大同小异,只是音乐、视频、语音、图文格式相对于文本消息构造的xml内容稍微复杂一点。
写一个构建文本消息的方法,代码如下:
/*** 构造文本消息* @param map 封装了解析结果的Map* @param content 文本消息内容* @return 文本消息XML字符串*/private static String buildTextMessage(Map<String, String> map, String content) {//发送方帐号String fromUserName = map.get("FromUserName");// 开发者微信号String toUserName = map.get("ToUserName");/*** 文本消息XML数据格式* <xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[fromUser]]></FromUserName><CreateTime>1348831860</CreateTime><MsgType><![CDATA[text]]></MsgType><Content><![CDATA[this is a test]]></Content><MsgId>1234567890123456</MsgId></xml>*/return String.format("<xml>" +"<ToUserName><![CDATA[%s]]></ToUserName>" +"<FromUserName><![CDATA[%s]]></FromUserName>" +"<CreateTime>%s</CreateTime>" +"<MsgType><![CDATA[text]]></MsgType>" +"<Content><![CDATA[%s]]></Content>" +"</xml>",fromUserName, toUserName, getMessageCreateTime(), content);}
回复图片消息
写一个构建图片消息的方法,代码如下:
/*** 构造图片消息* @param map 封装了解析结果的Map* @param mediaId 通过素材管理接口上传多媒体文件得到的id* @return 图片消息XML字符串*/private static String buildImageMessage(Map<String, String> map, String mediaId) {//发送方帐号String fromUserName = map.get("FromUserName");// 开发者微信号String toUserName = map.get("ToUserName");/*** 图片消息XML数据格式*<xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[fromUser]]></FromUserName><CreateTime>12345678</CreateTime><MsgType><![CDATA[image]]></MsgType><Image><MediaId><![CDATA[media_id]]></MediaId></Image></xml>*/return String.format("<xml>" +"<ToUserName><![CDATA[%s]]></ToUserName>" +"<FromUserName><![CDATA[%s]]></FromUserName>" +"<CreateTime>%s</CreateTime>" +"<MsgType><![CDATA[image]]></MsgType>" +"<Image>" +" <MediaId><![CDATA[%s]]></MediaId>" +"</Image>" +"</xml>",fromUserName, toUserName, getMessageCreateTime(), mediaId);}
回复音乐消息
写一个构建音乐消息的方法,代码如下:
/*** 构造音乐消息* @param map 封装了解析结果的Map* @param music 封装好的音乐消息内容* @return 音乐消息XML字符串*/private static String buildMusicMessage(Map<String, String> map, Music music) {//发送方帐号String fromUserName = map.get("FromUserName");// 开发者微信号String toUserName = map.get("ToUserName");/*** 音乐消息XML数据格式*<xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[fromUser]]></FromUserName><CreateTime>12345678</CreateTime><MsgType><![CDATA[music]]></MsgType><Music><Title><![CDATA[TITLE]]></Title><Description><![CDATA[DESCRIPTION]]></Description><MusicUrl><![CDATA[MUSIC_Url]]></MusicUrl><HQMusicUrl><![CDATA[HQ_MUSIC_Url]]></HQMusicUrl><ThumbMediaId><![CDATA[media_id]]></ThumbMediaId></Music></xml>*/return String.format("<xml>" +"<ToUserName><![CDATA[%s]]></ToUserName>" +"<FromUserName><![CDATA[%s]]></FromUserName>" +"<CreateTime>%s</CreateTime>" +"<MsgType><![CDATA[music]]></MsgType>" +"<Music>" +" <Title><![CDATA[%s]]></Title>" +" <Description><![CDATA[%s]]></Description>" +" <MusicUrl><![CDATA[%s]]></MusicUrl>" +" <HQMusicUrl><![CDATA[%s]]></HQMusicUrl>" +"</Music>" +"</xml>",fromUserName, toUserName, getMessageCreateTime(), music.title, music.description, music.musicUrl, music.hqMusicUrl);}
回复视频消息
写一个构建视频消息的方法,代码如下:
/*** 构造视频消息* @param map 封装了解析结果的Map* @param video 封装好的视频消息内容* @return 视频消息XML字符串*/private static String buildVideoMessage(Map<String, String> map, Video video) {//发送方帐号String fromUserName = map.get("FromUserName");// 开发者微信号String toUserName = map.get("ToUserName");/*** 音乐消息XML数据格式*<xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[fromUser]]></FromUserName><CreateTime>12345678</CreateTime><MsgType><![CDATA[video]]></MsgType><Video><MediaId><![CDATA[media_id]]></MediaId><Title><![CDATA[title]]></Title><Description><![CDATA[description]]></Description></Video></xml>*/return String.format("<xml>" +"<ToUserName><![CDATA[%s]]></ToUserName>" +"<FromUserName><![CDATA[%s]]></FromUserName>" +"<CreateTime>%s</CreateTime>" +"<MsgType><![CDATA[video]]></MsgType>" +"<Video>" +" <MediaId><![CDATA[%s]]></MediaId>" +" <Title><![CDATA[%s]]></Title>" +" <Description><![CDATA[%s]]></Description>" +"</Video>" +"</xml>",fromUserName, toUserName, getMessageCreateTime(), video.mediaId, video.title, video.description);}
回复语音消息
写一个构建语音消息的方法,代码如下:
/*** 构造语音消息* @param map 封装了解析结果的Map* @param mediaId 通过素材管理接口上传多媒体文件得到的id* @return 语音消息XML字符串*/private static String buildVoiceMessage(Map<String, String> map, String mediaId) {//发送方帐号String fromUserName = map.get("FromUserName");// 开发者微信号String toUserName = map.get("ToUserName");/*** 语音消息XML数据格式*<xml><ToUserName><![CDATA[toUser]]></ToUserName><FromUserName><![CDATA[fromUser]]></FromUserName><CreateTime>12345678</CreateTime><MsgType><![CDATA[voice]]></MsgType><Voice><MediaId><![CDATA[media_id]]></MediaId></Voice></xml>*/return String.format("<xml>" +"<ToUserName><![CDATA[%s]]></ToUserName>" +"<FromUserName><![CDATA[%s]]></FromUserName>" +"<CreateTime>%s</CreateTime>" +"<MsgType><![CDATA[voice]]></MsgType>" +"<Voice>" +" <MediaId><![CDATA[%s]]></MediaId>" +"</Voice>" +"</xml>",fromUserName, toUserName, getMessageCreateTime(), mediaId);}
回复图文消息
写一个构建图文消息的方法,代码如下:
/*** 构造图文消息* @param map 封装了解析结果的Map* @return 图文消息XML字符串*/private static String buildNewsMessage(Map<String, String> map) {String fromUserName = map.get("FromUserName");// 开发者微信号String toUserName = map.get("ToUserName");NewsItem item = new NewsItem();item.Title = "微信开发学习总结(一)——微信开发环境搭建";item.Description = "工欲善其事,必先利其器。要做微信公众号开发,那么要先准备好两样必不可少的东西:\n" +"\n" +" 1、要有一个用来测试的公众号。\n" +"\n" +" 2、用来调式代码的开发环境";item.PicUrl = "http://images2015.cnblogs.com/blog/289233/201601/289233-20160121164317343-2145023644.png";item.Url = "http://www.cnblogs.com/xdp-gacl/p/5149171.html";String itemContent1 = buildSingleItem(item); NewsItem item2 = new NewsItem();item2.Title = "微信开发学习总结(二)——微信开发入门";item2.Description = "微信服务器就相当于一个转发服务器,终端(手机、Pad等)发起请求至微信服务器,微信服务器然后将请求转发给我们的应用服务器。应用服务器处理完毕后,将响应数据回发给微信服务器,微信服务器再将具体响应信息回复到微信App终端。";item2.PicUrl = "";item2.Url = "http://www.cnblogs.com/xdp-gacl/p/5151857.html";String itemContent2 = buildSingleItem(item2);String content = String.format("<xml>\n" +"<ToUserName><![CDATA[%s]]></ToUserName>\n" +"<FromUserName><![CDATA[%s]]></FromUserName>\n" +"<CreateTime>%s</CreateTime>\n" +"<MsgType><![CDATA[news]]></MsgType>\n" +"<ArticleCount>%s</ArticleCount>\n" +"<Articles>\n" + "%s" +"</Articles>\n" +"</xml> ", fromUserName, toUserName, getMessageCreateTime(), 2, itemContent1 + itemContent2);return content;}/*** 生成图文消息的一条记录** @param item* @return*/private static String buildSingleItem(NewsItem item) {String itemContent = String.format("<item>\n" +"<Title><![CDATA[%s]]></Title> \n" +"<Description><![CDATA[%s]]></Description>\n" +"<PicUrl><![CDATA[%s]]></PicUrl>\n" +"<Url><![CDATA[%s]]></Url>\n" +"</item>", item.Title, item.Description, item.PicUrl, item.Url);return itemContent;}
根据上述提到的消息回复业务场景,我们可以写一个handleTextMessage方法来作为构造各种回复消息的处理入口,代码如下:
/*** 接收到文本消息后处理* @param map 封装了解析结果的Map* @return*/private static String handleTextMessage(Map<String, String> map) {//响应消息String responseMessage;// 消息内容String content = map.get("Content");switch (content) {case "文本":String msgText = "孤傲苍狼又要开始写博客总结了,欢迎朋友们访问我在博客园上面写的博客\n" +"<a href=\"http://www.cnblogs.com/xdp-gacl\">孤傲苍狼的博客</a>";responseMessage = buildTextMessage(map, msgText);break;case "图片"://通过素材管理接口上传图片时得到的media_idString imgMediaId = "dSQCiEHYB-pgi7ib5KpeoFlqpg09J31H28rex6xKgwWrln3HY0BTsoxnRV-xC_SQ";responseMessage = buildImageMessage(map, imgMediaId);break;case "语音"://通过素材管理接口上传语音文件时得到的media_idString voiceMediaId = "h3ul0TnwaRPut6Tl1Xlf0kk_9aUqtQvfM5Oq21unoWqJrwks505pkMGMbHnCHBBZ";responseMessage = buildVoiceMessage(map,voiceMediaId);break;case "图文":responseMessage = buildNewsMessage(map);break;case "音乐":Music music = new Music();music.title = "赵丽颖、许志安 - 乱世俱灭";music.description = "电视剧《蜀山战纪》插曲";music.musicUrl = "http://gacl.ngrok.natapp.cn/music/music.mp3";music.hqMusicUrl = "http://gacl.ngrok.natapp.cn/music/music.mp3";responseMessage = buildMusicMessage(map, music);break;case "视频":Video video = new Video();video.mediaId = "GqmIGpLu41rtwaY7WCVtJAL3ZbslzKiuLEXfWIKYDnHXGObH1CBH71xtgrGwyCa3";video.title = "小苹果";video.description = "小苹果搞笑视频";responseMessage = buildVideoMessage(map, video);break;default:responseMessage = buildWelcomeTextMessage(map);break;}//返回响应消息return responseMessage;}
到此,回复想消息的相关处理代码就编写完成了,将项目部署到Tomcat服务器进行测试,记得使用Ngrok将内网的服务器映射到外网,否则无法使用微信测试,如下:
使用微信进行消息回复测试,测试效果如下:
可以看到,每一种消息都正常响应了.
这里需要说一下图片,语音,视频的回复消息构造,这三种消息构造时的都需要一个mediaId,而这个mediaId是通过素材管理接口上传多媒体文件得到的,为了构造图片,语音,视频的这几种回复消息,我事先准备好了测试素材,如下图所示:
然后通过微信公众号平台提供的素材管理接口将图片,语音,视频上传到微信服务器上,上传成功后,微信服务器会给我们返回一个mediaId,用于标识上传成功的多媒体素材,上传素材的工具类代码如下:
1 package me.gacl.wx.util;2 3 import com.alibaba.fastjson.JSON;4 import com.alibaba.fastjson.JSONException;5 import com.alibaba.fastjson.JSONObject;6 import org.apache.commons.httpclient.HttpClient;7 import org.apache.commons.httpclient.HttpException;8 import org.apache.commons.httpclient.methods.PostMethod;9 import org.apache.commons.httpclient.methods.multipart.FilePart;10 import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;11 import org.apache.commons.httpclient.methods.multipart.Part;12 import org.apache.commons.httpclient.methods.multipart.StringPart;13 import org.apache.commons.httpclient.protocol.Protocol;14 import org.apache.commons.httpclient.protocol.SSLProtocolSocketFactory;15 import org.apache.http.HttpStatus;16 17 import javax.net.ssl.*;18 import java.io.*;19 import java.net.HttpURLConnection;20 import java.net.URL;21 import java.security.cert.CertificateException;22 import java.security.cert.X509Certificate;23 24 /**25 * Created by allen on 2016/1/29.26 */27 public class WeChatApiUtil {28 // token 接口(GET)29 private static final String ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";30 // 素材上传(POST)https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE31 private static final String UPLOAD_MEDIA = "https://api.weixin.qq.com/cgi-bin/media/upload";32 // 素材下载:不支持视频文件的下载(GET)33 private static final String DOWNLOAD_MEDIA = "http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=%s&media_id=%s";34 35 public static String getTokenUrl(String appId, String appSecret) {36 return String.format(ACCESS_TOKEN, appId, appSecret);37 }38 39 public static String getDownloadUrl(String token, String mediaId) {40 return String.format(DOWNLOAD_MEDIA, token, mediaId);41 }42 43 /**44 * 通用接口获取Token凭证45 *46 * @param appId47 * @param appSecret48 * @return49 */50 public static String getToken(String appId, String appSecret) {51 if (appId == null || appSecret == null) {52 return null;53 }54 55 String token = null;56 String tockenUrl = WeChatApiUtil.getTokenUrl(appId, appSecret);57 String response = httpsRequestToString(tockenUrl, "GET", null);58 JSONObject jsonObject = JSON.parseObject(response);59 if (null != jsonObject) {60 try {61 token = jsonObject.getString("access_token");62 } catch (JSONException e) {63 token = null;// 获取token失败64 }65 }66 return token;67 }68 69 /**70 * 微信服务器素材上传71 *72 * @param file 表单名称media73 * @param token access_token74 * @param type type只支持四种类型素材(video/image/voice/thumb)75 */76 public static JSONObject uploadMedia(File file, String token, String type) {77 if (file == null || token == null || type == null) {78 return null;79 }80 81 if (!file.exists()) {82 System.out.println("上传文件不存在,请检查!");83 return null;84 }85 86 String url = UPLOAD_MEDIA;87 JSONObject jsonObject = null;88 PostMethod post = new PostMethod(url);89 post.setRequestHeader("Connection", "Keep-Alive");90 post.setRequestHeader("Cache-Control", "no-cache");91 FilePart media;92 HttpClient httpClient = new HttpClient();93 //信任任何类型的证书94 Protocol myhttps = new Protocol("https", new SSLProtocolSocketFactory(), 443);95 Protocol.registerProtocol("https", myhttps);96 97 try {98 media = new FilePart("media", file);99 Part[] parts = new Part[]{new StringPart("access_token", token),
100 new StringPart("type", type), media};
101 MultipartRequestEntity entity = new MultipartRequestEntity(parts,
102 post.getParams());
103 post.setRequestEntity(entity);
104 int status = httpClient.executeMethod(post);
105 if (status == HttpStatus.SC_OK) {
106 String text = post.getResponseBodyAsString();
107 jsonObject = JSONObject.parseObject(text);
108 } else {
109 System.out.println("upload Media failure status is:" + status);
110 }
111 } catch (FileNotFoundException e) {
112 e.printStackTrace();
113 } catch (HttpException e) {
114 e.printStackTrace();
115 } catch (IOException e) {
116 e.printStackTrace();
117 }
118 return jsonObject;
119 }
120
121 /**
122 * 多媒体下载接口
123 *
124 * @param fileName 素材存储文件路径
125 * @param token 认证token
126 * @param mediaId 素材ID(对应上传后获取到的ID)
127 * @return 素材文件
128 * @comment 不支持视频文件的下载
129 */
130 public static File downloadMedia(String fileName, String token,
131 String mediaId) {
132 String url = getDownloadUrl(token, mediaId);
133 return httpRequestToFile(fileName, url, "GET", null);
134 }
135
136 /**
137 * 多媒体下载接口
138 *
139 * @param fileName 素材存储文件路径
140 * @param mediaId 素材ID(对应上传后获取到的ID)
141 * @return 素材文件
142 * @comment 不支持视频文件的下载
143 */
144 public static File downloadMedia(String fileName, String mediaId) {
145 String appId = "wxbe4d433e857e8bb1";
146 String appSecret = "ccbc82d560876711027b3d43a6f2ebda";
147 String token = WeChatApiUtil.getToken(appId, appSecret);
148 return downloadMedia(fileName,token,mediaId);
149 }
150
151 /**
152 * 以http方式发送请求,并将请求响应内容输出到文件
153 *
154 * @param path 请求路径
155 * @param method 请求方法
156 * @param body 请求数据
157 * @return 返回响应的存储到文件
158 */
159 public static File httpRequestToFile(String fileName, String path, String method, String body) {
160 if (fileName == null || path == null || method == null) {
161 return null;
162 }
163
164 File file = null;
165 HttpURLConnection conn = null;
166 InputStream inputStream = null;
167 FileOutputStream fileOut = null;
168 try {
169 URL url = new URL(path);
170 conn = (HttpURLConnection) url.openConnection();
171 conn.setDoOutput(true);
172 conn.setDoInput(true);
173 conn.setUseCaches(false);
174 conn.setRequestMethod(method);
175 if (null != body) {
176 OutputStream outputStream = conn.getOutputStream();
177 outputStream.write(body.getBytes("UTF-8"));
178 outputStream.close();
179 }
180
181 inputStream = conn.getInputStream();
182 if (inputStream != null) {
183 file = new File(fileName);
184 } else {
185 return file;
186 }
187
188 //写入到文件
189 fileOut = new FileOutputStream(file);
190 if (fileOut != null) {
191 int c = inputStream.read();
192 while (c != -1) {
193 fileOut.write(c);
194 c = inputStream.read();
195 }
196 }
197 } catch (Exception e) {
198 } finally {
199 if (conn != null) {
200 conn.disconnect();
201 }
202
203 /*
204 * 必须关闭文件流
205 * 否则JDK运行时,文件被占用其他进程无法访问
206 */
207 try {
208 inputStream.close();
209 fileOut.close();
210 } catch (IOException execption) {
211 }
212 }
213 return file;
214 }
215
216 /**
217 * 上传素材
218 * @param filePath 媒体文件路径(绝对路径)
219 * @param type 媒体文件类型,分别有图片(image)、语音(voice)、视频(video)和缩略图(thumb)
220 * @return
221 */
222 public static JSONObject uploadMedia(String filePath,String type){
223 File f = new File(filePath); // 获取本地文件
224 String appId = "wxbe4d433e857e8bb1";
225 String appSecret = "ccbc82d560876711027b3d43a6f2ebda";
226 String token = WeChatApiUtil.getToken(appId, appSecret);
227 JSONObject jsonObject = uploadMedia(f, token, type);
228 return jsonObject;
229 }
230
231 /**
232 * 发送请求以https方式发送请求并将请求响应内容以String方式返回
233 *
234 * @param path 请求路径
235 * @param method 请求方法
236 * @param body 请求数据体
237 * @return 请求响应内容转换成字符串信息
238 */
239 public static String httpsRequestToString(String path, String method, String body) {
240 if (path == null || method == null) {
241 return null;
242 }
243
244 String response = null;
245 InputStream inputStream = null;
246 InputStreamReader inputStreamReader = null;
247 BufferedReader bufferedReader = null;
248 HttpsURLConnection conn = null;
249 try {
250 TrustManager[] tm = {new JEEWeiXinX509TrustManager()};
251 SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
252 sslContext.init(null, tm, new java.security.SecureRandom());
253 SSLSocketFactory ssf = sslContext.getSocketFactory();
254 System.out.println(path);
255 URL url = new URL(path);
256 conn = (HttpsURLConnection) url.openConnection();
257 conn.setSSLSocketFactory(ssf);
258
259 conn.setDoOutput(true);
260 conn.setDoInput(true);
261 conn.setUseCaches(false);
262 conn.setRequestMethod(method);
263 if (null != body) {
264 OutputStream outputStream = conn.getOutputStream();
265 outputStream.write(body.getBytes("UTF-8"));
266 outputStream.close();
267 }
268
269 inputStream = conn.getInputStream();
270 inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
271 bufferedReader = new BufferedReader(inputStreamReader);
272 String str = null;
273 StringBuffer buffer = new StringBuffer();
274 while ((str = bufferedReader.readLine()) != null) {
275 buffer.append(str);
276 }
277
278 response = buffer.toString();
279 } catch (Exception e) {
280
281 } finally {
282 if (conn != null) {
283 conn.disconnect();
284 }
285 try {
286 bufferedReader.close();
287 inputStreamReader.close();
288 inputStream.close();
289 } catch (IOException execption) {
290
291 }
292 }
293 return response;
294 }
295
296 public static void main(String[] args) throws Exception{
297 //媒体文件路径
298 String filePath = "D:/JavaSoftwareDevelopeFolder/IntelliJ IDEA_Workspace/WxStudy/web/media/image/我.jpg";
299 //String filePath = "D:/JavaSoftwareDevelopeFolder/IntelliJ IDEA_Workspace/WxStudy/web/media/voice/voice.mp3";
300 //String filePath = "D:\\JavaSoftwareDevelopeFolder\\IntelliJ IDEA_Workspace\\WxStudy\\web\\media\\video\\小苹果.mp4";
301 //媒体文件类型
302 String type = "image";
303 //String type = "voice";
304 //String type = "video";
305 JSONObject uploadResult = uploadMedia(filePath, type);
306 //{"media_id":"dSQCiEHYB-pgi7ib5KpeoFlqpg09J31H28rex6xKgwWrln3HY0BTsoxnRV-xC_SQ","created_at":1455520569,"type":"image"}
307 System.out.println(uploadResult.toString());
308
309 //下载刚刚上传的图片以id命名
310 String media_id = uploadResult.getString("media_id");
311 File file = downloadMedia("D:/" + media_id + ".png", media_id);
312 System.out.println(file.getName());
313
314 }
315 }
316
317 class JEEWeiXinX509TrustManager implements X509TrustManager {
318 public void checkClientTrusted(X509Certificate[] chain, String authType)
319 throws CertificateException {
320 }
321
322 public void checkServerTrusted(X509Certificate[] chain, String authType)
323 throws CertificateException {
324 }
325
326 public X509Certificate[] getAcceptedIssuers() {
327 return null;
328 }
329 }
在工具类写一个main方法测试素材上传和下载,代码如下:
public static void main(String[] args) throws Exception{//媒体文件路径String filePath = "D:/JavaSoftwareDevelopeFolder/IntelliJ IDEA_Workspace/WxStudy/web/media/image/我.jpg";//String filePath = "D:/JavaSoftwareDevelopeFolder/IntelliJ IDEA_Workspace/WxStudy/web/media/voice/voice.mp3";//String filePath = "D:\\JavaSoftwareDevelopeFolder\\IntelliJ IDEA_Workspace\\WxStudy\\web\\media\\video\\小苹果.mp4";//媒体文件类型String type = "image";//String type = "voice";//String type = "video";JSONObject uploadResult = uploadMedia(filePath, type);//{"media_id":"dSQCiEHYB-pgi7ib5KpeoFlqpg09J31H28rex6xKgwWrln3HY0BTsoxnRV-xC_SQ","created_at":1455520569,"type":"image"}System.out.println(uploadResult.toString());//下载刚刚上传的图片以id命名String media_id = uploadResult.getString("media_id");File file = downloadMedia("D:/" + media_id + ".png", media_id);System.out.println(file.getName());}
运行结果如下:
可以看到,素材上传成功后,微信服务器就会返回一个media_id,用于标识上传后的文件.有了这个media_id后,我们就可以构建我们想要的图片,语音,视频回复消息了.
下面再说一下音乐的回复消息的构造,音乐素材也是我事先在服务器上准备了一个music.mp3音乐文件,并且保证可以正常使用外网访问,如下所示:
然后将MusicUrl和HQMusicUrl的地址指向了服务器上的music.mp3文件的访问地址,如下:
Music music = new Music();music.title = "赵丽颖、许志安 - 乱世俱灭";music.description = "电视剧《蜀山战纪》插曲";music.musicUrl = "http://gacl.ngrok.natapp.cn/media/music/music.mp3";music.hqMusicUrl = "http://gacl.ngrok.natapp.cn/media/music/music.mp3";responseMessage = buildMusicMessage(map, music);
这样就可以正常构造音乐回复消息了.
微信开发与代码的编写(二)相关推荐
- JavaWeb开发与代码的编写(二十四)
JavaWeb开发与代码的编写(二十四) JNDI数据源的配置 数据源的由来 在Java开发中,使用JDBC操作数据库的四个步骤如下: ①加载数据库驱动程序(Class.forName("数 ...
- JavaWeb开发与代码的编写(一)
JavaWeb开发与代码的编写(一) 绝对路径与相对路径 在JavaWeb开发中,常使用绝对路径的方式来引入JavaScript和CSS文件,这样可以避免因为目录变动导致引入文件找不到的情况,常用的做 ...
- php微信_分享一个完整的微信开发php代码
这篇文章主要为大家分享一个完整的微信开发php代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 本文实例为大家分享了微信开发php代码,供大家参考,具体内容如下 //封装成一个微信接口类 cla ...
- python微信开发入门_python tornado微信开发入门代码
本文实例为大家分享了python tornado微信开发的具体代码,供大家参考,具体内容如下 #微信入门代码 #!/usr/bin/env python2.7 # -*- coding: utf-8 ...
- 饥荒联机版Mod开发——配置代码环境(二)
饥荒联机版Mod开发--配置代码环境(二) 前言 下载VS Code和Lua插件 建立工作区 配置Lua插件 Git和GitHub(可选) 排除多余文件 删除scripts里多余文件 VS Code快 ...
- WebService开发与代码的编写
WebService开发与代码的编写 大家或多或少都听过 WebService(Web服务),有一段时间很多计算机期刊.书籍和网站都大肆的提及和宣传WebService技术,其中不乏很多吹嘘和做广告的 ...
- 【vue+pc端】实现微信扫码登录pc端,后端通过微信开发平台,前端生成二维码(仅供参考)
这两周的需求是通过微信扫码登录pc端,刚定下需求原型图还没出来前,后端特意发了微信开发平台的链接给我,关于如何生成二维码的文档,以及扫码跳转后如何传code给他. 请戳这里准备工作|微信开放文档 我最 ...
- 企业微信开发:获取 access_token(二)
前言 简单的用白话了解一下企业微信的作用,企业微信是腾讯微信团队为企业打造的专业办公管理工具.大致和钉钉差不多,适用于政府.企业等各类组织的一个产品,可以有效的帮您管理员工.个人感觉企业微信开发要 ...
- 微信接口测试号 php代码,模拟测试微信接口暨微信开发试验代码
要成为微信公众号(订阅号或服务号)的开发者,需要首先验证接口,这个可以在登录微信https://mp.weixin.qq.com后台后设置.但是我嫌麻烦,于是开发个接口类,包含验证函数(还有回复文本信 ...
最新文章
- TLS 1.3 Handshake Protocol (下)
- IDEA+scala+spark程序开发流程
- 安卓入门系列-02创建一个项目
- 微服务,我们如何与你相处
- oracle回退脚本怎么写_短视频爆款文案怎么写?130个短视频爆款文案、脚本范例分享!...
- python opencv光流跟踪_Opencv Python版学习笔记(四)光流跟踪之Gunnar Farneback’s 算法...
- 数据安全--安全网关
- opera安装java插件_欧朋浏览器Opera插件安装指南
- 从微信H5点击保存图片说起-微信图片下载
- No provider available from registry 192.168.126.129:2181 for service com.jt.service.DubboUserServic
- 与其去雄安买房,不如找中企动力建自己的平台
- 使用cloudflare防御假墙攻击和优化配置
- apk开发教程!安卓资深架构师分享学习经验及总结,技术详细介绍
- Android UI美化基本
- 禁止安装第三方应用(可对某个应用特殊处理),动态通过暗码改变是否能够安装第三方应用。拨号中输入*#数字#进入指定界面。
- 昆仑linux软件著作权,基于开源软件著作权
- 华为机试训练做题总结(四)
- 【销售】如何做好网络安全产品的销售
- 西门子博图指令(定时器操作四)
- 大数据应用项目创新大赛_创新创业大赛聚焦大数据应用 15个项目进入决赛