微信开发者模式实现图文消息发送
公众号类型
测试号
测试号现在仅支持使用预览接口,并不能实现群发。
【图片来源:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1481187827_i0l21截图】
实现
基于springboot
前端是自己根据图文消息(Article)的格式设计的,Article的内容如下:
【图片来源:官方文档截图】
1. 上传内容图片获取URL
【图片来源:官方文档截图】
前端使用bootstrap-fileinput上传图片,传到后台获取URL,然后将URL作为图片链接,放入wangEditor编辑器中。
(此处仅提供图片素材上传功能,暂未考虑视频、音频素材)
【注意:通过获取的微信服务器的URL插入编辑器时,可能会出现”未经允许“的图案,只需在当前html中加入
<meta name="referrer" content="never">
即可】
后台代码如下:
@PostMapping(value = "/wx/upload/img")
@ResponseBody
public String uploadImg(MultipartFile img) throws IOException {
JsonResult res = ResultUtil.success();
String url = wxService.postImg(img);
if (null != url) {
res.setData(url);
} else {
res = ResultUtil.error(ResultEnum.UPLOAD_IMG_FAILURE);
}
return res.toString();
}
/**
* 上传图文信息图片
*
* @param img 图片
* @return url
*/
@Override
@Nullable
public String postImg(MultipartFile img) throws IOException {
// 保存到本地,获取本地图片绝对路径
String filePath = FileUtil.saveFile("TEMP", img, "TEMP_"+DataUtil.getTimeStringSixBit()+DataUtil.getRandom(6)+".jpg");
JSONObject res = doPostForm(UPLOAD_IMG_URL.replace("ACCESS_TOKEN", wxTokenHelper.getToken().getToken()), filePath);
return res.getString("url");
}
/**
* POST请求 - Form格式数据
*
* @param url 请求地址
* @param filePath 文件本地路径
* @return
*/
public JSONObject doPostForm(String url, String filePath) throws IOException {
File file = new File(filePath);
if (!file.exists() || !file.isFile()) {
throw new IOException("文件不存在");
}
URL urlObj = new URL(url);
// 连接
HttpURLConnection con = (HttpURLConnection) urlObj.openConnection();
con.setRequestMethod("POST");
con.setDoInput(true);
con.setDoOutput(true);
con.setUseCaches(false);
// 设置请求头信息
con.setRequestProperty("Connection", "Keep-Alive");
con.setRequestProperty("Charset", "UTF-8");
// 设置边界
String BOUNDARY = "----------" + System.currentTimeMillis();
con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);
String sb = "--" +
BOUNDARY +
"\r\n" +
"Content-Disposition: form-data;name=\"file\";filename=\"" + file.getName() + "\"\r\n" +
"Content-Type:application/octet-stream\r\n\r\n";
byte[] head = sb.getBytes("utf-8");
// 获得输出流
OutputStream out = new DataOutputStream(con.getOutputStream());
// 输出表头
out.write(head);
// 文件正文部分
// 把文件已流文件的方式 推入到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();
// 结尾部分
byte[] foot = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("utf-8");// 定义最后数据分隔线
out.write(foot);
out.flush();
out.close();
StringBuilder buffer = new StringBuilder();
BufferedReader reader = null;
String result = null;
try {
// 定义BufferedReader输入流来读取URL的响应
reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
String line = null;
while ((line = reader.readLine()) != null) {
buffer.append(line);
}
result = buffer.toString();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
reader.close();
}
}
return JSONObject.parseObject(result);
}
2. 上传图文消息素材,得到media_id
【图片来源:官方文档截图】
/**
* 上传图文消息素材
*
* @param articles 图文消息集合
* @return media_id
*/
@Override
@Nullable
public String postNews(Article[] articles) {
JSONObject data = new JSONObject();
data.put("articles", articles);
JSONObject res = doPostStr(UPLOAD_NEWS_URL.replace("ACCESS_TOKEN", wxTokenHelper.getToken().getToken()), data.toJSONString());
return res.getString("media_id");
}
/**
* POST请求 - JSON格式数据
*
* @param url
* @param outStr
* @return
*/
public JSONObject doPostStr(String url, String outStr) {
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "application/json;charset=UTF-8");// 解决请求乱码问题
String response = restTemplate.postForEntity(url, new HttpEntity<String>(outStr, headers), String.class)
.getBody();
return JSON.parseObject(response);
}
/**
* @author kingsong
* @create 2018/9/26 9:00
* @desc 图文消息【推送】实体类
**/
public class Article {
private String thumb_media_id; // 图文消息缩略图media_id
private String author; // 图文消息作者
private String title; // 图文消息标题
private String content_source_url; // “阅读原文”链接,受安全限制,如需跳转Appstore,可以使用itun.es或appsto.re的短链服务,并在短链后增加 #wechat_redirect 后缀。
private String content; // 消息内容,支持HTML标签
private String digest; // 推送描述,如本字段为空,则默认抓去正文的前64个字
private int show_cover_pic; // 是否显示封面
public String getThumb_media_id() {
return thumb_media_id;
}
public void setThumb_media_id(String thumb_media_id) {
this.thumb_media_id = thumb_media_id;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent_source_url() {
return content_source_url;
}
public void setContent_source_url(String content_source_url) {
this.content_source_url = content_source_url;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getDigest() {
return digest;
}
public void setDigest(String digest) {
this.digest = digest;
}
public int getShow_cover_pic() {
return show_cover_pic;
}
public void setShow_cover_pic(int show_cover_pic) {
this.show_cover_pic = show_cover_pic;
}
}
3. 发送图文消息
测试号目前仅支持预览接口
【图片来源:官方文档截图】
后台代码如下:
@PostMapping(value = "/wx/send/new")
@ResponseBody
public String sendNew(String media_id) throws Exception {
JsonResult res = ResultUtil.success();
res.setData(wxService.sendNewsByOpenIds(userService.getAllOpenIds(), media_id));
return res.toString();
}
/**
* 根OpenID群发图文消息
*
* @param openIds OpenID集合
* @param media_id 图文消息媒体编号
* @return msg_id
* @throws Exception 发送失败
*/
@Override
public String sendNewsByOpenIds(List<String> openIds, String media_id) throws Exception {
JSONObject data = new JSONObject();
// 预览接口仅支持一个openID
data.put("touser", openIds.get(0));
Map<String, String> npnews = new HashMap<>();
npnews.put("media_id", media_id);
data.put("mpnews", npnews);
data.put("msgtype", "mpnews");
data.put("send_ignore_reprint", 0);
JSONObject res = doPostStr(SEND_NEWS_URL.replace("ACCESS_TOKEN", wxTokenHelper.getToken().getToken()), data.toJSONString());
if(0!=res.getIntValue("errcode")) {
// 发送失败
throw new CustomException(ResultEnum.SEND_NEWS_FAILURE);
} else {
return res.getString("msg_id");
}
}
若出现错误情况,可以查阅微信公众平台技术文档 - 全局返回码说明