0、寫在前面的話
如何實現微信平臺後臺管理中的,圖文消息發送功能?
大概的過程以下:
- 經過相似表單的形式,將文章各部份內容提交到後臺,封裝成一個實體類,並持久化到數據庫中
- 須要推送的時候,將不一樣的文章選擇取出交給後臺,由後臺組裝成規範化的數據結構,調用微信的圖文消息素材上傳和羣發接口
- 其中文章的主體部分,咱們採用UEditor富文本編輯器
本文主要針對模擬表單的圖片上傳,以及結合UEditor進行圖文內圖片上傳,進行重點說明。細節和具體代碼流程就再也不詳細展開了。
參考連接:
一、什麼是圖文消息
所謂圖文消息,就是咱們常在訂閱號中收到的以下圖所示的消息類型:
從
微信開發者文檔 - 消息管理 - 發送消息 - 羣發接口和原創校驗 - 上傳圖文消息素材 中能夠看到,圖文消息就是文章的集合,每條圖文消息包含的文章不得超過8條。
咱們從開發文檔中上傳圖文消息素材的POST格式來看,一個文章大概包含的內容有:
{
"articles": [
{
"thumb_media_id":"qI6_Ze_6PtV7svjolgs-rN6stStuHIjs9_DidOHaj0Q-mwvBelOXCFZiq2OsIU-p",
"author":"xxx",
"title":"Happy Day",
"content_source_url":"www.qq.com",
"content":"content",
"digest":"digest",
"show_cover_pic":1
},
{
"thumb_media_id":"qI6_Ze_6PtV7svjolgs-rN6stStuHIjs9_DidOHaj0Q-mwvBelOXCFZiq2OsIU-p",
"author":"xxx",
"title":"Happy Day",
"content_source_url":"www.qq.com",
"content":"content",
"digest":"digest",
"show_cover_pic":0
}
]
}
參數 |
是否必須 |
說明 |
Articles |
是 |
圖文消息,一個圖文消息支持1到8條圖文 |
thumb_media_id |
是 |
圖文消息縮略圖的media_id,能夠在基礎支持-上傳多媒體文件接口中得到 |
author |
否 |
圖文消息的做者 |
title |
是 |
圖文消息的標題 |
content_source_url |
否 |
在圖文消息頁面點擊「閱讀原文」後的頁面,受安全限制,如需跳轉Appstore,可使用itun.es或appsto.re的短鏈服務,並在短鏈後增長 #wechat_redirect 後綴。 |
content |
是 |
圖文消息頁面的內容,支持HTML標籤。具有微信支付權限的公衆號,可使用a標籤,其餘公衆號不能使用 |
digest |
否 |
圖文消息的描述 |
show_cover_pic |
否 |
是否顯示封面,1爲顯示,0爲不顯示 |
而本篇博客要解決的,就是
如何獲取上圖紅色部分的 thumb_media_id 和 content,後者主要也是闡述其內容中的圖片上傳過程。
二、羣發圖文消息的過程和UEditor使用
由微信的官方文檔可知,
羣發圖文消息的過程以下:
- 首先,調用接口上傳封面縮略圖素材,獲取thumb_media_id;(目標1)
- 而後將圖文消息中須要用到的圖片,使用上傳圖文消息內圖片接口,上傳成功並得到圖片 URL;(目標2)
- 上傳圖文消息素材,須要用到圖片時,請使用上一步獲取的圖片 URL;
- 使用對用戶標籤的羣發,或對 OpenID 列表的羣發,將圖文消息羣發出去,羣發時微信會進行原創校驗,並返回羣發操做結果;
- 在上述過程當中,若是須要,還能夠預覽圖文消息、查詢羣髮狀態,或刪除已羣發的消息等。
咱們的目標很明確,下面就開始按步驟進行說明。
2.1 啓用UEditor
UEditor是百度的一款富文本編輯器,用它的主要目的是由於它配置比較靈活,所見即所得的友好體驗。這裏只會提供編輯器部分,其餘諸如標題做者,須要自行在網頁添加,此處再也不闡述。
簡單說來,最終咱們是爲了實現一個相似提交表單的功能,
把各部份內容最後封裝到一個實體類中,而UEditor部分就是用來封裝文章的content的。
UEditor的安裝和工具欄設置等不是本文的討論重點,此處請自行谷歌,或按照官方文檔進行部署。網頁上部署好之後(以下圖中的部分3),再自行添加如標題輸入,做者輸入等input標籤,獲得效果以下:
參考連接:
2.2 封面和文章內圖片上傳
2.2.1 上傳封面縮略圖永久素材
在永久圖文消息中,要求縮略圖也是永久素材類型,因此咱們這裏調用的接口是 「新增其餘類型的永久素材的接口」,以下所示(這裏type咱們要用thumb):
http請求方式: POST,需使用https
https://api.weixin.qq.com/cgi-bin/material/add_material?access_token=ACCESS_TOKEN&type=TYPE
調用示例(使用curl命令,用FORM表單方式新增一個其餘類型的永久素材,curl命令的使用請自行查閱資料)
參數說明:
- access_token(必需) 調用接口憑證
- type(必需) 媒體文件類型,分別有圖片(image)、語音(voice)、視頻(video)和縮略圖(thumb)
- media(必需) form-data中媒體文件標識,有filename、filelength、content-type等信息
那麼問題來了,爲了不接口次數的頻繁使用和浪費,咱們在標題0中提到的過程是,將文章各部份內容提交到後臺,封裝成一個實體類,並持久化到數據庫中。須要推送的時候,再取出來調用微信接口進行上傳和推送(而不是編輯的時候就調用微信的接口)。
也就是說,縮略圖信息,都是提早寫好並整合封裝在某個類中的(或者放在數據庫中),這意味着咱們要在推送消息的時候從內容裏把須要上傳的東西抽出來再去調用接口,獲取返回值後再組裝微信格式的圖文消息發送。
而這裏的接口要求使用表單的形式提交才能生效,但用這種方式,密鑰信息accessToken就暴露在頁面中,而且也獲取不到上傳成功後的媒體ID。怎麼辦?
解決辦法(
參考連接
):
先把文件上傳到服務器(數據庫),從服務器獲取文件,而後用HttpURLConnection同下面微信調用接口的url創建鏈接
:
public static String uploadTemp(File file, String url) throws IOException {
String REQUEST_METHOD = "POST";
String result = null;
//請求地址
URL urlObj = new URL(url);
//鏈接
HttpURLConnection connection = (HttpURLConnection) urlObj.openConnection();
//設置關鍵值
//POST提交表單,默認是GET
connection.setRequestMethod(REQUEST_METHOD);
connection.setDoInput(true);
connection.setDoOutput(true);
//POST方式沒法使用緩存
connection.setUseCaches(false);
//設置請求頭信息
connection.setRequestProperty("Connection", "Keep-Alive");
connection.setRequestProperty("Charset", "UTF-8");
//設置邊界
String BOUNDARY = "----------" + System.currentTimeMillis();
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
//請求正文信息
//part1 開頭
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("--"); // 必須多兩道線
stringBuilder.append(BOUNDARY);
stringBuilder.append("\r\n");
stringBuilder.append("Content-Disposition: form-data;name=\"file\";filename=\"" + file.getName() + "\"\r\n");
stringBuilder.append("Content-Type:application/octet-stream\r\n\r\n");
byte[] head = stringBuilder.toString().getBytes("utf-8");
//得到輸出流
OutputStream out = new DataOutputStream(connection.getOutputStream());
//輸出表頭
out.write(head);
//part2 文件正文
//把文件以流文件的方式,推入到url中
DataInputStream in = new DataInputStream(new FileInputStream(file));
int bytes = 0;
byte[] bufferOut = new byte[1024];
while ((bytes = in.read(bufferOut)) != -1) {
out.write(bufferOut, 0, bytes);
}
in.close();
//part3 結尾部分
byte[] foot = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("utf-8");// 定義最後數據分隔線
out.write(foot);
out.flush();
out.close();
StringBuffer buffer = new StringBuffer();
BufferedReader reader = null;
try {
// 定義BufferedReader輸入流來讀取URL的響應
reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line = null;
while ((line = reader.readLine()) != null) {
buffer.append(line);
}
if (result == null) {
result = buffer.toString();
}
} catch (IOException e) {
log.error("發送POST請求出現異常!" + e);
e.printStackTrace();
throw new IOException("數據讀取異常");
} finally {
if(reader!=null) {
reader.close();
}
}
return result;
}
最終返回的result字符串,是一個JSON格式的微信服務器返回的值,其中咱們能夠提取其中的media_id。
2.2.2 結合UEditor實現圖文內圖片消息的圖片上傳
上傳圖文消息內的圖片,有專門的接口,這個接口上傳的圖片是不佔用永久素材的數量限制的。調用該接口,能夠得到圖片的URL,這裏的調用方式,也是和如上同樣使用表單模擬提交的方式,這裏主要講解的是,如何結合UEditor。
UEditor編輯器中的圖片上傳,咱們須要修改其上傳後的地址,也就是說但願它把圖片以流的形式給咱們本身寫的Action,再由咱們的Action把文件提交給微信服務器以獲取URL,再放到文章中去。
首先,更改編輯器的上傳圖片的後臺地址,重寫其js腳本部分的getActionUrl方法,定義跳轉到本身的Action:
ue = UE.getEditor("editor");
//重寫UEDITOR的getActionUrl 方法,定義本身的Action,上傳圖片保存至相應的位置
UE.Editor.prototype._bkGetActionUrl = UE.Editor.prototype.getActionUrl;
UE.Editor.prototype.getActionUrl = function (action) {
if (action == 'uploadimage' || action == 'uploadfile') {
var id = $('#carInfoId').val();
//修改定義本身的Action
return '/admin/news/do/ueditor_uploadNewsImage.q';
} else {
return this._bkGetActionUrl.call(this, action);
}
};
因爲提交過來的是tmp格式,直接調用微信接口會出現類型錯誤的返回值,因此在Action中咱們要將圖片格式轉換一下再調用微信接口:
參考連接:
/**
* ueditor編輯器上傳圖片到微信服務器
* <p>該部分是使用了百度的富文本web編輯器UEditor,以及微信的圖文消息內圖片上傳接口</p>
*
* @throws IOException
*/
public void ueditor_uploadNewsImage() throws IOException {
WeChatUtil weChatUtil = new WeChatUtil();
String accessToken = weChatUtil.getAccessToken();
HttpServletResponse response = ServletActionContext.getResponse();
//struts請求 包裝過濾器
MultiPartRequestWrapper wrapper = (MultiPartRequestWrapper) ServletActionContext.getRequest();
//獲取文件過濾器
File tmp = wrapper.getFiles("upfile")[0];
//得到上傳的文件名
String filename = wrapper.getFileNames("upfile")[0];
int suffixIndex = filename.indexOf(".");
//文件後綴肯定
if (suffixIndex == filename.lastIndexOf(".")) {
String suffix = filename.substring(suffixIndex);
File file = File.createTempFile("tmp", suffix);
FileCopyUtils.copy(tmp, file);
String imageUrl = weChatUtil.uploadImg(accessToken, file); //這裏最終仍是調用的表單模擬的那個方法
//若是獲取到了有效的imageUrl
if (imageUrl != null && imageUrl.substring(0, 4).equals("http")) {
String json = "{\"state\":\"SUCCESS\", \"url\":\"%s\", \"title\":\"%s\", \"original\":\"%s\"}";
json = String.format(json, imageUrl, filename, filename);
log.debug("上傳圖片返回信息:" + json);
response.getWriter().write(json);
}
}
}
另外,須要注意的是,UEditor要求
後端返回固定格式的返回碼才能實現圖片插入到文本編輯內容中。注意這裏的json返回碼格式,內部必須使用雙引號,使用單引號替代是無效的。
上面兩個就是該部分的核心內容,還有一個小地方須要修改,若是使用的Struts框架,那麼要注意,因爲Struts2框架默認使用Apache的Commons FileUpload組件和內建的FileUploadInterceptor攔截器實現文件上傳,將request中的文件域封裝到action中的一個File類型的屬性中,並刪除request中的原有文件域,所以直接用Ueditor上傳文件會失敗。
解決的方法是本身寫一個Filter替代Struts2的Filter,排除掉ueditor的controller.jsp文件。
參考連接:
public class MyStrutsFilter extends StrutsPrepareAndExecuteFilter {
public void doFilter(ServletRequest req, ServletResponse res,FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
//不過濾的url
String url = request.getRequestURI();
System.out.println(url);
try {
if (url.contains("/controller.jsp")) {
System.out.println("使用自定義的過濾器");
chain.doFilter(req, res);
} else {
System.out.println("使用默認的過濾器");
super.doFilter(req, res, chain);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.3 圖文素材上傳和發送
到這裏,咱們再來看下上傳圖文永久素材所須要的格式:
{
"articles": [
{
"thumb_media_id":"qI6_Ze_6PtV7svjolgs-rN6stStuHIjs9_DidOHaj0Q-mwvBelOXCFZiq2OsIU-p",
"author":"xxx",
"title":"Happy Day",
"content_source_url":"www.qq.com",
"content":"content",
"digest":"digest",
"show_cover_pic":1
},
{
"thumb_media_id":"qI6_Ze_6PtV7svjolgs-rN6stStuHIjs9_DidOHaj0Q-mwvBelOXCFZiq2OsIU-p",
"author":"xxx",
"title":"Happy Day",
"content_source_url":"www.qq.com",
"content":"content",
"digest":"digest",
"show_cover_pic":0
}
]
}
- thumb_media_id 上傳封面縮略圖後返回的media_id,咱們已經說明了
- author 這個寫個input就能夠搞定
- title 同上
- content_source_url 非必需
- content 這裏就是咱們用UEditor編輯器中的內容了,其中重點是圖片上傳,咱們已經說明了
- digest 摘要,加input解決
- show_cover_pic 非必需