微信開發學習總結(三)——開發微信公衆號的最基本功能——普通消息的接收和回覆

1、微信公衆平臺消息管理接口介紹

  要實現微信公衆號的普通消息的接收和回覆,咱們須要先熟悉微信公衆平臺API中消息接口部分,點此進入,點擊後將進入到【消息管理】部分,以下圖所示:php

  

  對於普通消息的接收和回覆咱們只須要關注上圖中的"接收消息——接收普通消息"和"發送消息——被動回覆消息"html

1.一、消息接收

  先來講說接收消息, 當普通微信用戶向公衆帳號發消息時,微信服務器會先接收到用戶發送的消息,而後將用戶消息按照指定的XML格式組裝好數據,最後POST消息的XML數據包到開發者填寫的URL上。java

  接收到的普通消息的消息類型目前有如下幾種:web

  1 文本消息
  2 圖片消息
  3 語音消息
  4 視頻消息
  5 小視頻消息
  6 地理位置消息
  7 連接消息apache

  每一種消息類型都有其指定的XML數據格式,這7種消息的xml格式請到官方文檔查看,有具體的格式定義和屬性說明。格式很簡單,基本共有屬性包括ToUserName、FromUserName、CreateTime、MsgType、MsgId,而且每種類型有本身特殊的屬性。json

  接收消息的過程其實就是獲取post請求的這個xml,而後對這個xml進行分析的過程。post請求的入口仍是以前提到的微信公衆號接入的那個地址,整個公衆號的全部請求都會走這個入口,只是接入時是get請求,其它狀況下是post請求。api

1.二、消息回覆

  微信服務器在將用戶的消息發給公衆號的開發者服務器地址後,會等待開發者服務器回覆響應消息。微信服務器在五秒內收不到響應會斷掉鏈接,而且從新發起請求,總共重試三次。服務器

  假如服務器沒法保證在五秒內處理並回復,必須作出下述回覆,這樣微信服務器纔不會對此做任何處理,而且不會發起重試(這種狀況下,可使用客服消息接口進行異步回覆),不然,將出現嚴重的錯誤提示。詳見下面說明:微信

  一、(推薦方式)直接回復success
  二、直接回復空串(指字節長度爲0的空字符串,而不是XML結構體中content字段的內容爲空)

 一旦遇到如下狀況,微信都會在公衆號會話中,向用戶下發系統提示「該公衆號暫時沒法提供服務,請稍後再試」:微信開發

  一、開發者在5秒內未回覆任何內容
  二、開發者回覆了異常數據,好比JSON數據等

  另外,請注意,回覆圖片等多媒體消息時須要預先經過素材管理接口上傳臨時素材到微信服務器,可使用素材管理中的臨時素材,也可使用永久素材。

  消息回覆目前支持回覆文本、圖片、圖文、語音、視頻、音樂,每一種類型的消息都有特定的XML數據格式。這幾種回覆消息的xml數據格式請參考官方文檔,有具體的格式定義和屬性說明。格式很簡單,基本共有屬性包括ToUserName、FromUserName、CreateTime、MsgType,而且每種類型有本身特殊的屬性。

 2、微信公衆號的普通消息的接收和回覆

2.一、接收消息

  接收消息和被動回覆消息這兩個動做是不分家的,這原本就是一個交互場景,通常狀況就是公衆號經過分析接收到的消息,會給出對應的回覆。

  以前說過了,接收消息的過程其實就是獲取微信服務器經過post請求的發送給咱們公衆號服務器的xml數據,而後咱們的公衆號服務器再對這個xml進行解析處理的過程。爲了方便解析XML數據,咱們藉助於dom4j,dom4j是一個十分優秀的JavaXML API,具備性能優異、功能強大和極其易使用的特色,是用來讀寫XML文件的。針對微信服務器發來的xml請求數據,咱們寫一個parseXml方法來處理,parseXml方法的代碼以下:

複製代碼

1  /**
 2      * 解析微信發來的請求(XML)
 3      *
 4      * @param request 封裝了請求信息的HttpServletRequest對象
 5      * @return map 解析結果
 6      * @throws Exception
 7      */
 8     public static Map<String, String> parseXml(HttpServletRequest request) throws Exception {
 9         // 將解析結果存儲在HashMap中
10         Map<String, String> map = new HashMap<String, String>();
11         // 從request中取得輸入流
12         InputStream inputStream = request.getInputStream();
13         // 讀取輸入流
14         SAXReader reader = new SAXReader();
15         Document document = reader.read(inputStream);
16         // 獲得xml根元素
17         Element root = document.getRootElement();
18         // 獲得根元素的全部子節點
19         List<Element> elementList = root.elements();
20 
21         // 遍歷全部子節點
22         for (Element e : elementList) {
23             System.out.println(e.getName() + "|" + e.getText());
24             map.put(e.getName(), e.getText());
25         }
26 
27         // 釋放資源
28         inputStream.close();
29         inputStream = null;
30         return map;
31     }

複製代碼

  而後在處理微信請求的入口servlet的doPost方法中調用parseXml方法便可,調用代碼以下:

複製代碼

1  /**
 2      * 處理微信服務器發來的消息
 3      */
 4     protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
 5         // TODO 接收、處理、響應由微信服務器轉發的用戶發送給公衆賬號的消息
 6         // 將請求、響應的編碼均設置爲UTF-8(防止中文亂碼)
 7         request.setCharacterEncoding("UTF-8");
 8         response.setCharacterEncoding("UTF-8");
 9         System.out.println("請求進入");
10         String responseMessage;
11         try {
12             //解析微信發來的請求,將解析後的結果封裝成Map返回
13             Map<String,String> map = MessageHandlerUtil.parseXml(request);
14             System.out.println("開始構造響應消息");
15             responseMessage = MessageHandlerUtil.buildResponseMessage(map);
16             System.out.println(responseMessage);
17             if(responseMessage.equals("")){
18                 responseMessage ="未正確響應";
19             }
20         } catch (Exception e) {
21             e.printStackTrace();
22             System.out.println("發生異常:"+ e.getMessage());
23             responseMessage ="未正確響應";
24         }
25         //發送響應消息
26         response.getWriter().println(responseMessage);
27     }

複製代碼

  這樣咱們就完成了消息的接收,消息接收以後,咱們就要根據消息類型進行響應了,寫一個根據消息類型構造返回消息的方法,代碼以下:

複製代碼

1 /**
 2      * 根據消息類型構造返回消息
 3      * @param map 封裝瞭解析結果的Map
 4      * @return responseMessage(響應消息)
 5      */
 6     public static String buildResponseMessage(Map map) {
 7         //響應消息
 8         String responseMessage = "";
 9         //獲得消息類型
10         String msgType = map.get("MsgType").toString();
11         System.out.println("MsgType:" + msgType);
12         //消息類型
13         MessageType messageEnumType = MessageType.valueOf(MessageType.class, msgType.toUpperCase());
14         switch (messageEnumType) {
15             case TEXT:
16                 //處理文本消息
17                 responseMessage = handleTextMessage(map);
18                 break;
19             case IMAGE:
20                 //處理圖片消息
21                 responseMessage = handleImageMessage(map);
22                 break;
23             case VOICE:
24                 //處理語音消息
25                 responseMessage = handleVoiceMessage(map);
26                 break;
27             case VIDEO:
28                 //處理視頻消息
29                 responseMessage = handleVideoMessage(map);
30                 break;
31             case SHORTVIDEO:
32                 //處理小視頻消息
33                 responseMessage = handleSmallVideoMessage(map);
34                 break;
35             case LOCATION:
36                 //處理位置消息
37                 responseMessage = handleLocationMessage(map);
38                 break;
39             case LINK:
40                 //處理連接消息
41                 responseMessage = handleLinkMessage(map);
42                 break;
43             case EVENT:
44                 //處理事件消息,用戶在關注與取消關注公衆號時,微信會向咱們的公衆號服務器發送事件消息,開發者接收到事件消息後就能夠給用戶下發歡迎消息
45                 responseMessage = handleEventMessage(map);
46             default:
47                 break;
48         }
49         //返回響應消息
50         return responseMessage;
51     }

複製代碼

  這樣咱們就完成了根據消息類型進行響應了,在處理微信請求的入口servlet的doPost方法中調用buildResponseMessage方法便可,doPost方法的完整代碼在上面已經貼出來了.buildResponseMessage方法中使用到了一個MessageType類,這是一個消息類型枚舉類,MessageType類的代碼以下:

複製代碼

1 /**
 2  * 接收到的消息類型
 3  */
 4 public enum MessageType {
 5     TEXT,//文本消息
 6     IMAGE,//圖片消息
 7     VOICE,//語音消息
 8     VIDEO,//視頻消息
 9     SHORTVIDEO,//小視頻消息
10     LOCATION,//地理位置消息
11     LINK,//連接消息
12     EVENT//事件消息
13 }

複製代碼

2.二、回覆消息

  下面我基於這樣一個業務場景來演示構造回覆的消息,接收到文本消息"文本",回覆文本消息;接收到「圖片」,回覆圖片消息;接收到「語音」,回覆語音消息;接收到「視頻」,回覆視頻消息;接收到「音樂」,回覆音樂消息;接收到「圖文」,回覆圖文消息。下面具體說明各類消息的構建,只貼出核心代碼,一些輔助代碼類請自行下載項目代碼參考.

2.2.一、回覆文本消息

  接收的文本消息的XML數據格式以下:

複製代碼

1 <xml>
2  <ToUserName><![CDATA[toUser]]></ToUserName>
3  <FromUserName><![CDATA[fromUser]]></FromUserName> 
4  <CreateTime>1348831860</CreateTime>
5  <MsgType><![CDATA[text]]></MsgType>
6  <Content><![CDATA[this is a test]]></Content>
7  <MsgId>1234567890123456</MsgId>
8 </xml>

複製代碼

  回覆的文本消息的XML數據格式以下:

複製代碼

1 <xml>
2 <ToUserName><![CDATA[發消息的人,即訂閱者]]></ToUserName>
3 <FromUserName><![CDATA[微信公衆號自己]]></FromUserName>
4 <CreateTime>消息建立時間(整形)</CreateTime>
5 <MsgType><![CDATA[text]]></MsgType>
6 <Content><![CDATA[消息內容]]></Content>
7 </xml>

複製代碼

  其中接收消息格式中的ToUserName即是回覆消息的FromUserName,接收消息格式中的FromUserName即是回覆消息的ToUserName。CreateTime爲消息發送的時間戳。MsgType爲消息類型,文本爲text。Content爲消息內容。具體每一種類型消息的回覆,就是構造此種類型的xml格式內容,格式大同小異,只是音樂、視頻、語音、圖文格式相對於文本消息構造的xml內容稍微複雜一點。

  寫一個構建文本消息的方法,代碼以下:

複製代碼

1  /**
 2      * 構造文本消息
 3      * @param map 封裝瞭解析結果的Map
 4      * @param content 文本消息內容
 5      * @return 文本消息XML字符串
 6      */
 7     private static String buildTextMessage(Map<String, String> map, String content) {
 8         //發送方賬號
 9         String fromUserName = map.get("FromUserName");
10         // 開發者微信號
11         String toUserName = map.get("ToUserName");
12         /**
13          * 文本消息XML數據格式
14          * <xml>
15          <ToUserName><![CDATA[toUser]]></ToUserName>
16          <FromUserName><![CDATA[fromUser]]></FromUserName>
17          <CreateTime>1348831860</CreateTime>
18          <MsgType><![CDATA[text]]></MsgType>
19          <Content><![CDATA[this is a test]]></Content>
20          <MsgId>1234567890123456</MsgId>
21          </xml>
22          */
23         return String.format(
24                 "<xml>" +
25                         "<ToUserName><![CDATA[%s]]></ToUserName>" +
26                         "<FromUserName><![CDATA[%s]]></FromUserName>" +
27                         "<CreateTime>%s</CreateTime>" +
28                         "<MsgType><![CDATA[text]]></MsgType>" +
29                         "<Content><![CDATA[%s]]></Content>" +
30                         "</xml>",
31                 fromUserName, toUserName, getMessageCreateTime(), content);
32     }

複製代碼

  2.2.二、回覆圖片消息

  寫一個構建圖片消息的方法,代碼以下:

複製代碼

1  /**
 2      * 構造圖片消息
 3      * @param map 封裝瞭解析結果的Map
 4      * @param mediaId 經過素材管理接口上傳多媒體文件獲得的id
 5      * @return 圖片消息XML字符串
 6      */
 7     private static String buildImageMessage(Map<String, String> map, String mediaId) {
 8         //發送方賬號
 9         String fromUserName = map.get("FromUserName");
10         // 開發者微信號
11         String toUserName = map.get("ToUserName");
12         /**
13          * 圖片消息XML數據格式
14          *<xml>
15          <ToUserName><![CDATA[toUser]]></ToUserName>
16          <FromUserName><![CDATA[fromUser]]></FromUserName>
17          <CreateTime>12345678</CreateTime>
18          <MsgType><![CDATA[image]]></MsgType>
19          <Image>
20          <MediaId><![CDATA[media_id]]></MediaId>
21          </Image>
22          </xml>
23          */
24         return String.format(
25                 "<xml>" +
26                         "<ToUserName><![CDATA[%s]]></ToUserName>" +
27                         "<FromUserName><![CDATA[%s]]></FromUserName>" +
28                         "<CreateTime>%s</CreateTime>" +
29                         "<MsgType><![CDATA[image]]></MsgType>" +
30                         "<Image>" +
31                         "   <MediaId><![CDATA[%s]]></MediaId>" +
32                         "</Image>" +
33                         "</xml>",
34                 fromUserName, toUserName, getMessageCreateTime(), mediaId);
35     }

複製代碼

  2.2.三、回覆音樂消息

  寫一個構建音樂消息的方法,代碼以下:

複製代碼

1  /**
 2      * 構造音樂消息
 3      * @param map 封裝瞭解析結果的Map
 4      * @param music 封裝好的音樂消息內容
 5      * @return 音樂消息XML字符串
 6      */
 7     private static String buildMusicMessage(Map<String, String> map, Music music) {
 8         //發送方賬號
 9         String fromUserName = map.get("FromUserName");
10         // 開發者微信號
11         String toUserName = map.get("ToUserName");
12         /**
13          * 音樂消息XML數據格式
14          *<xml>
15          <ToUserName><![CDATA[toUser]]></ToUserName>
16          <FromUserName><![CDATA[fromUser]]></FromUserName>
17          <CreateTime>12345678</CreateTime>
18          <MsgType><![CDATA[music]]></MsgType>
19          <Music>
20          <Title><![CDATA[TITLE]]></Title>
21          <Description><![CDATA[DESCRIPTION]]></Description>
22          <MusicUrl><![CDATA[MUSIC_Url]]></MusicUrl>
23          <HQMusicUrl><![CDATA[HQ_MUSIC_Url]]></HQMusicUrl>
24          <ThumbMediaId><![CDATA[media_id]]></ThumbMediaId>
25          </Music>
26          </xml>
27          */
28         return String.format(
29                 "<xml>" +
30                         "<ToUserName><![CDATA[%s]]></ToUserName>" +
31                         "<FromUserName><![CDATA[%s]]></FromUserName>" +
32                         "<CreateTime>%s</CreateTime>" +
33                         "<MsgType><![CDATA[music]]></MsgType>" +
34                         "<Music>" +
35                         "   <Title><![CDATA[%s]]></Title>" +
36                         "   <Description><![CDATA[%s]]></Description>" +
37                         "   <MusicUrl><![CDATA[%s]]></MusicUrl>" +
38                         "   <HQMusicUrl><![CDATA[%s]]></HQMusicUrl>" +
39                         "</Music>" +
40                         "</xml>",
41                 fromUserName, toUserName, getMessageCreateTime(), music.title, music.description, music.musicUrl, music.hqMusicUrl);
42     }

複製代碼

  2.2.四、回覆視頻消息

  寫一個構建視頻消息的方法,代碼以下:

複製代碼

1  /**
 2      * 構造視頻消息
 3      * @param map 封裝瞭解析結果的Map
 4      * @param video 封裝好的視頻消息內容
 5      * @return 視頻消息XML字符串
 6      */
 7     private static String buildVideoMessage(Map<String, String> map, Video video) {
 8         //發送方賬號
 9         String fromUserName = map.get("FromUserName");
10         // 開發者微信號
11         String toUserName = map.get("ToUserName");
12         /**
13          * 音樂消息XML數據格式
14          *<xml>
15          <ToUserName><![CDATA[toUser]]></ToUserName>
16          <FromUserName><![CDATA[fromUser]]></FromUserName>
17          <CreateTime>12345678</CreateTime>
18          <MsgType><![CDATA[video]]></MsgType>
19          <Video>
20          <MediaId><![CDATA[media_id]]></MediaId>
21          <Title><![CDATA[title]]></Title>
22          <Description><![CDATA[description]]></Description>
23          </Video>
24          </xml>
25          */
26         return String.format(
27                 "<xml>" +
28                         "<ToUserName><![CDATA[%s]]></ToUserName>" +
29                         "<FromUserName><![CDATA[%s]]></FromUserName>" +
30                         "<CreateTime>%s</CreateTime>" +
31                         "<MsgType><![CDATA[video]]></MsgType>" +
32                         "<Video>" +
33                         "   <MediaId><![CDATA[%s]]></MediaId>" +
34                         "   <Title><![CDATA[%s]]></Title>" +
35                         "   <Description><![CDATA[%s]]></Description>" +
36                         "</Video>" +
37                         "</xml>",
38                 fromUserName, toUserName, getMessageCreateTime(), video.mediaId, video.title, video.description);
39     }

複製代碼

  2.2.五、回覆語音消息

  寫一個構建語音消息的方法,代碼以下:

複製代碼

1  /**
 2      * 構造語音消息
 3      * @param map 封裝瞭解析結果的Map
 4      * @param mediaId 經過素材管理接口上傳多媒體文件獲得的id
 5      * @return 語音消息XML字符串
 6      */
 7     private static String buildVoiceMessage(Map<String, String> map, String mediaId) {
 8         //發送方賬號
 9         String fromUserName = map.get("FromUserName");
10         // 開發者微信號
11         String toUserName = map.get("ToUserName");
12         /**
13          * 語音消息XML數據格式
14          *<xml>
15          <ToUserName><![CDATA[toUser]]></ToUserName>
16          <FromUserName><![CDATA[fromUser]]></FromUserName>
17          <CreateTime>12345678</CreateTime>
18          <MsgType><![CDATA[voice]]></MsgType>
19          <Voice>
20          <MediaId><![CDATA[media_id]]></MediaId>
21          </Voice>
22          </xml>
23          */
24         return String.format(
25                 "<xml>" +
26                         "<ToUserName><![CDATA[%s]]></ToUserName>" +
27                         "<FromUserName><![CDATA[%s]]></FromUserName>" +
28                         "<CreateTime>%s</CreateTime>" +
29                         "<MsgType><![CDATA[voice]]></MsgType>" +
30                         "<Voice>" +
31                         "   <MediaId><![CDATA[%s]]></MediaId>" +
32                         "</Voice>" +
33                         "</xml>",
34                 fromUserName, toUserName, getMessageCreateTime(), mediaId);
35     }

複製代碼

  2.2.六、回覆圖文消息

  寫一個構建圖文消息的方法,代碼以下:

複製代碼

1   /**
 2      * 構造圖文消息
 3      * @param map 封裝瞭解析結果的Map
 4      * @return 圖文消息XML字符串
 5      */
 6     private static String buildNewsMessage(Map<String, String> map) {
 7         String fromUserName = map.get("FromUserName");
 8         // 開發者微信號
 9         String toUserName = map.get("ToUserName");
10         NewsItem item = new NewsItem();
11         item.Title = "微信開發學習總結(一)——微信開發環境搭建";
12         item.Description = "工欲善其事,必先利其器。要作微信公衆號開發,那麼要先準備好兩樣必不可少的東西:\n" +
13                 "\n" +
14                 "  一、要有一個用來測試的公衆號。\n" +
15                 "\n" +
16                 "  二、用來調式代碼的開發環境";
17         item.PicUrl = "http://images2015.cnblogs.com/blog/289233/201601/289233-20160121164317343-2145023644.png";
18         item.Url = "http://www.cnblogs.com/xdp-gacl/p/5149171.html";
19         String itemContent1 = buildSingleItem(item);
20 
21         NewsItem item2 = new NewsItem();
22         item2.Title = "微信開發學習總結(二)——微信開發入門";
23         item2.Description = "微信服務器就至關於一個轉發服務器,終端(手機、Pad等)發起請求至微信服務器,微信服務器而後將請求轉發給咱們的應用服務器。應用服務器處理完畢後,將響應數據回發給微信服務器,微信服務器再將具體響應信息回覆到微信App終端。";
24         item2.PicUrl = "";
25         item2.Url = "http://www.cnblogs.com/xdp-gacl/p/5151857.html";
26         String itemContent2 = buildSingleItem(item2);
27 
28 
29         String content = String.format("<xml>\n" +
30                 "<ToUserName><![CDATA[%s]]></ToUserName>\n" +
31                 "<FromUserName><![CDATA[%s]]></FromUserName>\n" +
32                 "<CreateTime>%s</CreateTime>\n" +
33                 "<MsgType><![CDATA[news]]></MsgType>\n" +
34                 "<ArticleCount>%s</ArticleCount>\n" +
35                 "<Articles>\n" + "%s" +
36                 "</Articles>\n" +
37                 "</xml> ", fromUserName, toUserName, getMessageCreateTime(), 2, itemContent1 + itemContent2);
38         return content;
39 
40     }
41 
42     /**
43      * 生成圖文消息的一條記錄
44      *
45      * @param item
46      * @return
47      */
48     private static String buildSingleItem(NewsItem item) {
49         String itemContent = String.format("<item>\n" +
50                 "<Title><![CDATA[%s]]></Title> \n" +
51                 "<Description><![CDATA[%s]]></Description>\n" +
52                 "<PicUrl><![CDATA[%s]]></PicUrl>\n" +
53                 "<Url><![CDATA[%s]]></Url>\n" +
54                 "</item>", item.Title, item.Description, item.PicUrl, item.Url);
55         return itemContent;
56     }

複製代碼

  根據上述提到的消息回覆業務場景,咱們能夠寫一個handleTextMessage方法來做爲構造各類回覆消息的處理入口,代碼以下:

複製代碼

1  /**
 2      * 接收到文本消息後處理
 3      * @param map 封裝瞭解析結果的Map
 4      * @return
 5      */
 6     private static String handleTextMessage(Map<String, String> map) {
 7         //響應消息
 8         String responseMessage;
 9         // 消息內容
10         String content = map.get("Content");
11         switch (content) {
12             case "文本":
13                 String msgText = "孤傲蒼狼又要開始寫博客總結了,歡迎朋友們訪問我在博客園上面寫的博客\n" +
14                         "<a href=\"http://www.cnblogs.com/xdp-gacl\">孤傲蒼狼的博客</a>";
15                 responseMessage = buildTextMessage(map, msgText);
16                 break;
17             case "圖片":
18                 //經過素材管理接口上傳圖片時獲得的media_id
19                 String imgMediaId = "dSQCiEHYB-pgi7ib5KpeoFlqpg09J31H28rex6xKgwWrln3HY0BTsoxnRV-xC_SQ";
20                 responseMessage = buildImageMessage(map, imgMediaId);
21                 break;
22             case "語音":
23                 //經過素材管理接口上傳語音文件時獲得的media_id
24                 String voiceMediaId = "h3ul0TnwaRPut6Tl1Xlf0kk_9aUqtQvfM5Oq21unoWqJrwks505pkMGMbHnCHBBZ";
25                 responseMessage = buildVoiceMessage(map,voiceMediaId);
26                 break;
27             case "圖文":
28                 responseMessage = buildNewsMessage(map);
29                 break;
30             case "音樂":
31                 Music music = new Music();
32                 music.title = "趙麗穎、許志安 - 亂世俱滅";
33                 music.description = "電視劇《蜀山戰紀》插曲";
34                 music.musicUrl = "http://gacl.ngrok.natapp.cn/music/music.mp3";
35                 music.hqMusicUrl = "http://gacl.ngrok.natapp.cn/music/music.mp3";
36                 responseMessage = buildMusicMessage(map, music);
37                 break;
38             case "視頻":
39                 Video video = new Video();
40                 video.mediaId = "GqmIGpLu41rtwaY7WCVtJAL3ZbslzKiuLEXfWIKYDnHXGObH1CBH71xtgrGwyCa3";
41                 video.title = "小蘋果";
42                 video.description = "小蘋果搞笑視頻";
43                 responseMessage = buildVideoMessage(map, video);
44                 break;
45             default:
46                 responseMessage = buildWelcomeTextMessage(map);
47                 break;
48 
49         }
50         //返回響應消息
51         return responseMessage;
52     }

複製代碼

  到此,回覆想消息的相關處理代碼就編寫完成了,將項目部署到Tomcat服務器進行測試,記得使用Ngrok將內網的服務器映射到外網,不然沒法使用微信測試,以下:

  

  關於Ngrok的使用方式以前的《微信開發學習總結(一)——微信開發環境搭建》博客中已經介紹過了,這裏就再也不重複講解了,沒了解過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=TYPE
 31     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 appId
 47      * @param appSecret
 48      * @return
 49      */
 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  表單名稱media
 73      * @param token access_token
 74      * @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方法測試素材上傳和下載,代碼以下:

複製代碼

1 public static void main(String[] args) throws Exception{
 2         //媒體文件路徑
 3         String filePath = "D:/JavaSoftwareDevelopeFolder/IntelliJ IDEA_Workspace/WxStudy/web/media/image/我.jpg";
 4         //String filePath = "D:/JavaSoftwareDevelopeFolder/IntelliJ IDEA_Workspace/WxStudy/web/media/voice/voice.mp3";
 5         //String filePath = "D:\\JavaSoftwareDevelopeFolder\\IntelliJ IDEA_Workspace\\WxStudy\\web\\media\\video\\小蘋果.mp4";
 6         //媒體文件類型
 7         String type = "image";
 8         //String type = "voice";
 9         //String type = "video";
10         JSONObject uploadResult = uploadMedia(filePath, type);
11         //{"media_id":"dSQCiEHYB-pgi7ib5KpeoFlqpg09J31H28rex6xKgwWrln3HY0BTsoxnRV-xC_SQ","created_at":1455520569,"type":"image"}
12         System.out.println(uploadResult.toString());
13 
14         //下載剛剛上傳的圖片以id命名
15         String media_id = uploadResult.getString("media_id");
16         File file = downloadMedia("D:/" + media_id + ".png", media_id);
17         System.out.println(file.getName());
18 
19     }

複製代碼

  運行結果以下:

  

   能夠看到,素材上傳成功後,微信服務器就會返回一個media_id,用於標識上傳後的文件.有了這個media_id後,咱們就能夠構建咱們想要的圖片,語音,視頻回覆消息了.

  下面再說一下音樂的回覆消息的構造,音樂素材也是我事先在服務器上準備了一個music.mp3音樂文件,而且保證能夠正常使用外網訪問,以下所示:

  

  而後將MusicUrl和HQMusicUrl的地址指向了服務器上的music.mp3文件的訪問地址,以下:

複製代碼

1 Music music = new Music();
2 music.title = "趙麗穎、許志安 - 亂世俱滅";
3 music.description = "電視劇《蜀山戰紀》插曲";
4 music.musicUrl = "http://gacl.ngrok.natapp.cn/media/music/music.mp3";
5 music.hqMusicUrl = "http://gacl.ngrok.natapp.cn/media/music/music.mp3";
6 responseMessage = buildMusicMessage(map, music);

複製代碼

  這樣就能夠正常構造音樂回覆消息了.

相關文章
相關標籤/搜索