本篇文章实现模板、图文、文本、音乐、图片推送,前提是已经搭建了微信开发环境。读完本文后,实现的主要效果如下

  1. 在测试账号中配置模板
    登录测试公众号/正式公众号(认证后的服务号),测试公众号:模板消息接口->新增测试模板中添加模板,正式公众号:在功能->模板消息中添加模板,模板可以在模板库中选择,如果没有你需要的模板,可以申请添加,一个月可以申请三条。模板添加成功后,有个模板ID(用于接口调用)。
    具体如何配置可以参看官方文档:https://mp.weixin.qq.com/wiki 中消息管理->发送消息-模板消息接口。

  2. 配置好后上代码:

Controller:

package com.zhongyitang.vehicle.common.weixin;import com.zhongyitang.vehicle.common.weixin.utils.MessageHandlerUtil;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.weixin4j.spring.web.WeixinJieruController;
import org.weixin4j.util.TokenUtil;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;@Controller
@RequestMapping("/check")
public class JieruController {private static final String token = "oliver"; //开发者自行定义的Tooken@GetMapping("/signature")public void get(HttpServletRequest request, HttpServletResponse response) throws IOException {//消息来源可靠性验证String signature = request.getParameter("signature");// 微信加密签名String timestamp = request.getParameter("timestamp");// 时间戳String nonce = request.getParameter("nonce");       // 随机数//Token为weixin4j.properties中配置的Token
//        String token = TokenUtil.get();//1.验证消息真实性//http://mp.weixin.qq.com/wiki/index.php?title=验证消息真实性//成为开发者验证String echostr = request.getParameter("echostr");//确认此次GET请求来自微信服务器,原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败if (TokenUtil.checkSignature(token, signature, timestamp, nonce)) {response.getWriter().write(echostr);}}//接收微信消息@PostMapping("/signature")public void post(HttpServletRequest request, HttpServletResponse response) throws IOException {//消息来源可靠性验证String signature = request.getParameter("signature");// 微信加密签名String timestamp = request.getParameter("timestamp");// 时间戳String nonce = request.getParameter("nonce");       // 随机数//Token为weixin4j.properties中配置的Token
//        String token = TokenUtil.get();//确认此次GET请求来自微信服务器,原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败if (!TokenUtil.checkSignature(token, signature, timestamp, nonce)) {//消息不可靠,直接返回response.getWriter().write("");return;}String result = "";try {response.setCharacterEncoding("UTF-8");response.setContentType("text/xml");Map<String,String> map = MessageHandlerUtil.parseXml(request);System.out.println("开始构造消息");result = MessageHandlerUtil.buildResponseMessage(map);System.out.println("result:"+result);if(result.equals("")){result = "未正确响应或返回为空";System.out.println("result:"+result);}else{response.getWriter().println(result);}} catch (Exception e) {e.printStackTrace();System.out.println("发生异常:"+ e.getMessage());}}}

模板基类:

/**1. 模板基类2. @author CLiang3.  */
public class WxTemplate {private String template_id;//模板IDprivate String touser;//目标客户private String url;//用户点击模板信息的跳转页面private String topcolor;//字体颜色private Map<String,TemplateData> data;//模板里的数据public String getTemplate_id() {return template_id;}public void setTemplate_id(String template_id) {this.template_id = template_id;}public String getTouser() {return touser;}public void setTouser(String touser) {this.touser = touser;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getTopcolor() {return topcolor;}public void setTopcolor(String topcolor) {this.topcolor = topcolor;}public Map<String,TemplateData> getData() {return data;}public void setData(Map<String,TemplateData> data) {this.data = data;}
}

一条模板包含多条数据,模板数据类封装:

/**4. 模板数据5. @author By Oliver_Deng6.  */
public class TemplateData {private String value;//模板显示值private String color;//模板显示颜色public String getValue() {return value;}public void setValue(String value) {this.value = value;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}
}
  1. 获取ACCESS_TOKEN
    看文档,发送模板信息接口为:https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN ,前期模板数据都准备好了,现在缺少ACCESS_TOKEN,缺什么就去获取什么,查看文档可知获取ACCESS_TOKEN接口为:https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET ,现在就封装个获取ACCESS_TOKEN的请求方法:
/*** 发送模板消息前获取token* @param template_id_short 模板库中模板的编号* @param t* @param m*/
public static void sendMessageBefore(String template_id_short,WxTemplate t,Map<String,TemplateData> m){String accessToken = WeChatUtil.getAccessToken();System.out.println("accessToKen:         "+accessToken);t.setData(m);sendMessage(t,accessToken);
}

上述代码中用到的WeChatUtils:

package com.zhongyitang.vehicle.common.weixin.utils;import net.sf.json.JSONObject;
import java.util.Date;public class WeChatUtil {//URL验证时使用的token
//    public static final String TOKEN = "wolfcode";//appidpublic static final String APPID = "你自己的";//secretpublic static final String SECRET = "你自己的";//创建菜单接口地址public static final String CREATE_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";//获取access_token的接口地址public static final String GET_ACCESSTOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";//发送模板消息的接口public static final String SEND_TEMPLATE_URL = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";//缓存的access_tokenprivate static String accessToken;//access_token的失效时间private static long expiresTime;/*** 获取accessToken* @return*/public static String getAccessToken(){//判断accessToken是否已经过期,如果过期需要重新获取if(accessToken==null||expiresTime < new Date().getTime()){//发起请求获取accessTokenString result = HTTPUtils.doGet(GET_ACCESSTOKEN_URL.replace("APPID", APPID).replace("APPSECRET", SECRET));//把json字符串转换为json对象
//            JSONObject json = JSON.parseObject(result);JSONObject json = JSONObject.fromObject(result);//缓存accessTokenaccessToken = json.getString("access_token");//设置accessToken的失效时间long expires_in = json.getLong("expires_in");//失效时间 = 当前时间 + 有效期(提前一分钟)expiresTime = new Date().getTime()+ (expires_in-60) * 1000;}return accessToken;}}

注意:ACCESS_TOKEN有请求次数限制,而且会在获取后7200秒后自动失效,所以要妥善保存好ACCESS_TOKEN。

  1. 我的测试模板:
{{first.DATA}}
违章时间:{{violationTime.DATA}}
违章地点:{{violationAddress.DATA}}
违章内容:{{violationType.DATA}}
罚款金额:{{violationFine.DATA}}
扣分情况:{{violationPoints.DATA}}
{{remark.DATA}}
  1. 发送模板:
/*** 发送模板消息调用实例* @param map 封装了解析结果的Map
* @return 空 */private static String buildTempMessage(Map<String, String> map) {//发送方帐号String fromUserName = map.get("FromUserName");// 开发者微信号String toUserName = map.get("ToUserName");WxTemplate template = new WxTemplate();template.setUrl("www.baidu.com");template.setTouser(fromUserName);template.setTopcolor("#000000");template.setTemplate_id("9E9deiteIUNNJADRPJa5A5TbcrqFRK0UmsvIG2-0RbE"); //模板IDMap<String,TemplateData> m = new HashMap<String, TemplateData>();TemplateData first = new TemplateData();first.setColor("#000000");first.setValue("您好,您车牌号为"京56K453"的车辆有新的违章信息");m.put("first", first);TemplateData keyword1 = new TemplateData();keyword1.setColor("#328392");keyword1.setValue("2014年5月07日 16:00");m.put("violationTime", keyword1);TemplateData keyword2 = new TemplateData();keyword2.setColor("#328392");keyword2.setValue("北京市西城区开阳桥北天桥北向南");m.put("violationAddress", keyword2);TemplateData keyword3 = new TemplateData();keyword3.setColor("#328392");keyword3.setValue("不按规定停车");m.put("violationType", keyword3);TemplateData keyword4 = new TemplateData();keyword4.setColor("#328392");keyword4.setValue("200元");m.put("violationFine", keyword4);TemplateData keyword5 = new TemplateData();keyword5.setColor("#328392");keyword5.setValue("扣3分");m.put("violationPoints", keyword5);TemplateData remark = new TemplateData();remark.setColor("#929232");remark.setValue("点击查看更多内容");m.put("remark", remark);sendMessageBefore("", template, m);return "";}

上述代码中 setTemplate_id 需要改成你自己的模板ID。
上述代码中用到的方法:

/*** 发送模板消息前获取token* @param template_id_short 模板库中模板的编号* @param t* @param m*/public static void sendMessageBefore(String template_id_short,WxTemplate t,Map<String,TemplateData> m){String accessToken = WeChatUtil.getAccessToken();System.out.println("accessToKen:         "+accessToken);t.setData(m);sendMessage(t,accessToken);}/*** 发送模板消息* @param t* @param accessToken* @return*/public static int sendMessage(WxTemplate t,String accessToken) {int result = 0;// 拼装发送模板消息的urlString url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN".replace("ACCESS_TOKEN", accessToken);// 将模板对象转换成json字符串String jsonMenu = JSONObject.toJSONString(t);// 调用接口发送模板String response = WeChatApiUtil.httpsRequestToString(url, "POST", jsonMenu);JSONObject jsonObject = JSON.parseObject(response);
//        JSONObject jsonObject = httpRequest(url, "POST", jsonMenu);if (null != jsonObject) {if (0 != jsonObject.getIntValue("errcode")) {result = jsonObject.getIntValue("errcode");System.out.println("发送模板消息失败 errcode:{"+jsonObject.getIntValue("errcode")+"} errmsg:{"+jsonObject.getString("errmsg")+"}");}}return result;}
  1. 完成上述代码可以实现“模板“功能:

主要工具类:
MessageHandlerUtil:

package com.zhongyitang.vehicle.common.weixin.utils;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zhongyitang.vehicle.common.weixin.vo.*;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** 消息处理工具类* Created By Oliver_Deng*/
public class MessageHandlerUtil {/*** 解析微信发来的请求(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;}/*** 根据消息类型构造返回消息* @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;}/*** 接收到文本消息后处理* @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=\"https://www.baidu.com\">搜索飞梦名车</a>";responseMessage = buildTextMessage(map, msgText);break;case "模板":responseMessage = buildTempMessage(map);break;case "图片"://通过素材管理接口上传图片时得到的media_idString imgMediaId = "_Oeog5ScPfhcDtpOvCCwZMDDrA5mjS45NrxmtgYDqgv9ABQ9aqPtMAc2jq-66SOu";responseMessage = buildImageMessage(map, imgMediaId);break;case "图文":responseMessage = buildNewsMessage(map);break;case "音乐":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);break;default:responseMessage = buildWelcomeTextMessage(map);break;}//返回响应消息return responseMessage;}/*** 生成消息创建时间 (整型)* @return 消息创建时间*/private static String getMessageCreateTime() {Date dt = new Date();// 如果不需要格式,可直接用dt,dt就是当前系统时间DateFormat df = new SimpleDateFormat("yyyyMMddhhmm");// 设置显示格式String nowTime = df.format(dt);long dd = (long) 0;try {dd = df.parse(nowTime).getTime();} catch (Exception e) {}return String.valueOf(dd);}/*** 构建提示消息* @param map 封装了解析结果的Map* @return responseMessageXml*/private static String buildWelcomeTextMessage(Map<String, String> map) {String responseMessageXml;String fromUserName = map.get("FromUserName");// 开发者微信号String toUserName = map.get("ToUserName");responseMessageXml = 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(),"感谢您关注我的公众号,请回复如下关键词来使用公众号提供的服务:\n文本\n模板\n图片\n音乐\n图文");return responseMessageXml;}/*** 构造文本消息* @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* @return 空 */private static String buildTempMessage(Map<String, String> map) {//发送方帐号String fromUserName = map.get("FromUserName");// 开发者微信号String toUserName = map.get("ToUserName");WxTemplate template = new WxTemplate();template.setUrl("www.baidu.com");template.setTouser(fromUserName);template.setTopcolor("#000000");template.setTemplate_id("9E9deiteIUNNJADRPJa5A5TbcrqFRK0UmsvIG2-0RbE"); //模板IDMap<String,TemplateData> m = new HashMap<String, TemplateData>();TemplateData first = new TemplateData();first.setColor("#000000");first.setValue("您好,您车牌号为"京56K453"的车辆有新的违章信息");m.put("first", first);TemplateData keyword1 = new TemplateData();keyword1.setColor("#328392");keyword1.setValue("2014年5月07日 16:00");m.put("violationTime", keyword1);TemplateData keyword2 = new TemplateData();keyword2.setColor("#328392");keyword2.setValue("北京市西城区开阳桥北天桥北向南");m.put("violationAddress", keyword2);TemplateData keyword3 = new TemplateData();keyword3.setColor("#328392");keyword3.setValue("不按规定停车");m.put("violationType", keyword3);TemplateData keyword4 = new TemplateData();keyword4.setColor("#328392");keyword4.setValue("200元");m.put("violationFine", keyword4);TemplateData keyword5 = new TemplateData();keyword5.setColor("#328392");keyword5.setValue("扣3分");m.put("violationPoints", keyword5);TemplateData remark = new TemplateData();remark.setColor("#929232");remark.setValue("点击查看更多内容");m.put("remark", remark);sendMessageBefore("", template, m);return "";}/*** 发送模板消息前获取token* @param template_id_short 模板库中模板的编号* @param t* @param m*/public static void sendMessageBefore(String template_id_short,WxTemplate t,Map<String,TemplateData> m){String accessToken = WeChatUtil.getAccessToken();System.out.println("accessToKen:         "+accessToken);t.setData(m);sendMessage(t,accessToken);}/*** 发送模板消息* @param t* @param accessToken* @return*/public static int sendMessage(WxTemplate t,String accessToken) {int result = 0;// 拼装发送模板消息的urlString url = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN".replace("ACCESS_TOKEN", accessToken);// 将模板对象转换成json字符串String jsonMenu = JSONObject.toJSONString(t);// 调用接口发送模板String response = WeChatApiUtil.httpsRequestToString(url, "POST", jsonMenu);JSONObject jsonObject = JSON.parseObject(response);
//        JSONObject jsonObject = httpRequest(url, "POST", jsonMenu);if (null != jsonObject) {if (0 != jsonObject.getIntValue("errcode")) {result = jsonObject.getIntValue("errcode");System.out.println("发送模板消息失败 errcode:{"+jsonObject.getIntValue("errcode")+"} errmsg:{"+jsonObject.getString("errmsg")+"}");}}return result;}/*** 构造图片消息* @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;}/*** 处理接收到图片消息** @param map* @return*/private static String handleImageMessage(Map<String, String> map) {String picUrl = map.get("PicUrl");String mediaId = map.get("MediaId");System.out.print("picUrl:" + picUrl);System.out.print("mediaId:" + mediaId);String result = String.format("已收到您发来的图片,图片Url为:%s\n图片素材Id为:%s", picUrl, mediaId);return buildTextMessage(map, result);}/*** 处理接收到语音消息* @param map* @return*/private static String handleVoiceMessage(Map<String, String> map) {String format = map.get("Format");String mediaId = map.get("MediaId");System.out.print("format:" + format);System.out.print("mediaId:" + mediaId);String result = String.format("已收到您发来的语音,语音格式为:%s\n语音素材Id为:%s", format, mediaId);return buildTextMessage(map, result);}/*** 处理接收到的视频消息* @param map* @return*/private static String handleVideoMessage(Map<String, String> map) {String thumbMediaId = map.get("ThumbMediaId");String mediaId = map.get("MediaId");System.out.print("thumbMediaId:" + thumbMediaId);System.out.print("mediaId:" + mediaId);String result = String.format("已收到您发来的视频,视频中的素材ID为:%s\n视频Id为:%s", thumbMediaId, mediaId);return buildTextMessage(map, result);}/*** 处理接收到的小视频消息* @param map* @return*/private static String handleSmallVideoMessage(Map<String, String> map) {String thumbMediaId = map.get("ThumbMediaId");String mediaId = map.get("MediaId");System.out.print("thumbMediaId:" + thumbMediaId);System.out.print("mediaId:" + mediaId);String result = String.format("已收到您发来的小视频,小视频中素材ID为:%s,\n小视频Id为:%s", thumbMediaId, mediaId);return buildTextMessage(map, result);}/*** 处理接收到的地理位置消息* @param map* @return*/private static String handleLocationMessage(Map<String, String> map) {String latitude = map.get("Location_X");  //纬度String longitude = map.get("Location_Y");  //经度String label = map.get("Label");  //地理位置精度String result = String.format("纬度:%s\n经度:%s\n地理位置:%s", latitude, longitude, label);return buildTextMessage(map, result);}/*** 处理接收到的链接消息* @param map* @return*/private static String handleLinkMessage(Map<String, String> map) {String title = map.get("Title");String description = map.get("Description");String url = map.get("Url");String result = String.format("已收到您发来的链接,链接标题为:%s,\n描述为:%s\n,链接地址为:%s", title, description, url);return buildTextMessage(map, result);}/*** 处理消息Message* @param map 封装了解析结果的Map* @return*/private static String handleEventMessage(Map<String, String> map) {String content = map.get("Event");String responseMessage;switch (content) {case "SCAN"://扫码进入responseMessage="";break;case "SUBSCRIBE"://关注responseMessage=buildWelcomeTextMessage(map);break;case "TEMPLATESENDJOBFINISH"://模板发送完成responseMessage="";break;default:responseMessage = buildWelcomeTextMessage(map);break;}return responseMessage;}}

HTTPUtils:

package com.zhongyitang.vehicle.common.weixin.utils;import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;/*** By Oliver_Deng*/
public class HTTPUtils {private static final int TIMEOUT_IN_MILLIONS = 5000;public interface CallBack{void onRequestComplete(String result);}/*** 异步的Get请求** @param urlStr* @param callBack*/public static void doGetAsyn(final String urlStr, final CallBack callBack){new Thread(){public void run(){try{String result = doGet(urlStr);if (callBack != null){callBack.onRequestComplete(result);}} catch (Exception e){e.printStackTrace();}};}.start();}/*** 异步的Post请求* @param urlStr* @param params* @param callBack* @throws Exception*/public static void doPostAsyn(final String urlStr, final String params,final CallBack callBack) throws Exception{new Thread(){public void run(){try{String result = doPost(urlStr, params);if (callBack != null){callBack.onRequestComplete(result);}} catch (Exception e){e.printStackTrace();}};}.start();}/*** Get请求,获得返回数据** @param urlStr* @return* @throws Exception*/public static String doGet(String urlStr){URL url = null;HttpURLConnection conn = null;InputStream is = null;ByteArrayOutputStream baos = null;try{url = new URL(urlStr);conn = (HttpURLConnection) url.openConnection();conn.setReadTimeout(TIMEOUT_IN_MILLIONS);conn.setConnectTimeout(TIMEOUT_IN_MILLIONS);conn.setRequestMethod("GET");conn.setRequestProperty("accept", "*/*");conn.setRequestProperty("connection", "Keep-Alive");if (conn.getResponseCode() == 200){is = conn.getInputStream();baos = new ByteArrayOutputStream();int len = -1;byte[] buf = new byte[128];while ((len = is.read(buf)) != -1){baos.write(buf, 0, len);}baos.flush();return baos.toString();} else{throw new RuntimeException(" responseCode is not 200 ... ");}} catch (Exception e){e.printStackTrace();} finally{try{if (is != null)is.close();} catch (IOException e){}try{if (baos != null)baos.close();} catch (IOException e){}conn.disconnect();}return null ;}/*** 向指定 URL 发送POST方法的请求** @param url*            发送请求的 URL* @param param*            请求参数,请求参数应该是 name1=value1&name2=value2 的形式。* @return 所代表远程资源的响应结果* @throws Exception*/public static String doPost(String url, String param){PrintWriter out = null;BufferedReader in = null;String result = "";try{URL realUrl = new URL(url);// 打开和URL之间的连接HttpURLConnection conn = (HttpURLConnection) realUrl.openConnection();// 设置通用的请求属性conn.setRequestProperty("accept", "*/*");conn.setRequestProperty("connection", "Keep-Alive");conn.setRequestMethod("POST");conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");conn.setRequestProperty("charset", "utf-8");conn.setUseCaches(false);// 发送POST请求必须设置如下两行conn.setDoOutput(true);conn.setDoInput(true);conn.setReadTimeout(TIMEOUT_IN_MILLIONS);conn.setConnectTimeout(TIMEOUT_IN_MILLIONS);if (param != null && !param.trim().equals("")){// 获取URLConnection对象对应的输出流out = new PrintWriter(conn.getOutputStream());// 发送请求参数out.print(param);// flush输出流的缓冲out.flush();}// 定义BufferedReader输入流来读取URL的响应in = new BufferedReader(new InputStreamReader(conn.getInputStream()));String line;while ((line = in.readLine()) != null){result += line;}} catch (Exception e){e.printStackTrace();}// 使用finally块来关闭输出流、输入流finally{try{if (out != null){out.close();}if (in != null){in.close();}} catch (IOException ex){ex.printStackTrace();}}return result;}
}

MessageType:

package com.zhongyitang.vehicle.common.weixin.utils;/*** 接收到的消息类型* By Oliver_Deng*/
public enum MessageType {TEXT,//文本消息IMAGE,//图片消息VOICE,//语音消息VIDEO,//视频消息SHORTVIDEO,//小视频消息LOCATION,//地理位置消息LINK,//链接消息EVENT//事件消息
}

WeChatApiUtil:

package com.zhongyitang.vehicle.common.weixin.utils;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;import javax.net.ssl.*;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;/*** Created by Oliver_Deng*/
public class WeChatApiUtil {// token 接口(GET)private static final String ACCESS_TOKEN = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";// 素材上传(POST)https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPEprivate static final String UPLOAD_MEDIA = "https://api.weixin.qq.com/cgi-bin/media/upload";// 素材下载:不支持视频文件的下载(GET)private static final String DOWNLOAD_MEDIA = "http://file.api.weixin.qq.com/cgi-bin/media/get?access_token=%s&media_id=%s";public static String getTokenUrl(String appId, String appSecret) {return String.format(ACCESS_TOKEN, appId, appSecret);}public static String getDownloadUrl(String token, String mediaId) {return String.format(DOWNLOAD_MEDIA, token, mediaId);}/*** 通用接口获取Token凭证** @param appId* @param appSecret* @return*/public static String getToken(String appId, String appSecret) {if (appId == null || appSecret == null) {return null;}String token = null;String tockenUrl = WeChatApiUtil.getTokenUrl(appId, appSecret);String response = httpsRequestToString(tockenUrl, "GET", null);JSONObject jsonObject = JSON.parseObject(response);if (null != jsonObject) {try {token = jsonObject.getString("access_token");} catch (JSONException e) {token = null;// 获取token失败}}return token;}/*** 微信服务器素材上传** @param file  表单名称media* @param token access_token* @param type  type只支持四种类型素材(video/image/voice/thumb)*/
//    public static JSONObject uploadMedia(File file, String token, String type) {//        if (file == null || token == null || type == null) {//            return null;
//        }
//
//        if (!file.exists()) {//            System.out.println("上传文件不存在,请检查!");
//            return null;
//        }
//
//        String url = UPLOAD_MEDIA;
//        JSONObject jsonObject = null;
//        PostMethod post = new PostMethod(url);
//        post.setRequestHeader("Connection", "Keep-Alive");
//        post.setRequestHeader("Cache-Control", "no-cache");
//        FilePart media;
//        HttpClient httpClient = new HttpClient();
//        //信任任何类型的证书
//        Protocol myhttps = new Protocol("https", new SSLProtocolSocketFactory(), 443);
//        Protocol.registerProtocol("https", myhttps);
//
//        try {//            media = new FilePart("media", file);
//            Part[] parts = new Part[]{new StringPart("access_token", token),
//                    new StringPart("type", type), media};
//            MultipartRequestEntity entity = new MultipartRequestEntity(parts,
//                    post.getParams());
//            post.setRequestEntity(entity);
//            int status = httpClient.executeMethod(post);
//            if (status == HttpStatus.SC_OK) {//                String text = post.getResponseBodyAsString();
//                jsonObject = JSONObject.parseObject(text);
//            } else {//                System.out.println("upload Media failure status is:" + status);
//            }
//        } catch (FileNotFoundException e) {//            e.printStackTrace();
//        } catch (HttpException e) {//            e.printStackTrace();
//        } catch (IOException e) {//            e.printStackTrace();
//        }
//        return jsonObject;
//    }/*** 多媒体下载接口** @param fileName 素材存储文件路径* @param token    认证token* @param mediaId  素材ID(对应上传后获取到的ID)* @return 素材文件* @comment 不支持视频文件的下载*/public static File downloadMedia(String fileName, String token,String mediaId) {String url = getDownloadUrl(token, mediaId);return httpRequestToFile(fileName, url, "GET", null);}/*** 多媒体下载接口** @param fileName 素材存储文件路径* @param mediaId  素材ID(对应上传后获取到的ID)* @return 素材文件* @comment 不支持视频文件的下载*/public static File downloadMedia(String fileName, String mediaId) {String appId = "wx2cf6086315216ab0";String appSecret = "a4362add70c148da80e7442100782e4e";String token = WeChatApiUtil.getToken(appId, appSecret);return downloadMedia(fileName,token,mediaId);}/*** 以http方式发送请求,并将请求响应内容输出到文件** @param path   请求路径* @param method 请求方法* @param body   请求数据* @return 返回响应的存储到文件*/public static File httpRequestToFile(String fileName, String path, String method, String body) {if (fileName == null || path == null || method == null) {return null;}File file = null;HttpURLConnection conn = null;InputStream inputStream = null;FileOutputStream fileOut = null;try {URL url = new URL(path);conn = (HttpURLConnection) url.openConnection();conn.setDoOutput(true);conn.setDoInput(true);conn.setUseCaches(false);conn.setRequestMethod(method);if (null != body) {OutputStream outputStream = conn.getOutputStream();outputStream.write(body.getBytes("UTF-8"));outputStream.close();}inputStream = conn.getInputStream();if (inputStream != null) {file = new File(fileName);} else {return file;}//写入到文件fileOut = new FileOutputStream(file);if (fileOut != null) {int c = inputStream.read();while (c != -1) {fileOut.write(c);c = inputStream.read();}}} catch (Exception e) {} finally {if (conn != null) {conn.disconnect();}/** 必须关闭文件流* 否则JDK运行时,文件被占用其他进程无法访问*/try {inputStream.close();fileOut.close();} catch (IOException execption) {}}return file;}/*** 上传素材* @param filePath 媒体文件路径(绝对路径)* @param type 媒体文件类型,分别有图片(image)、语音(voice)、视频(video)和缩略图(thumb)* @return*/
//    public static JSONObject uploadMedia(String filePath,String type){//        File f = new File(filePath); // 获取本地文件
//        String appId = "wx2cf6086315216ab0";
//        String appSecret = "a4362add70c148da80e7442100782e4e";
//        String token = WeChatApiUtil.getToken(appId, appSecret);
//        JSONObject jsonObject = uploadMedia(f, token, type);
//        return jsonObject;
//    }/*** 发送请求以https方式发送请求并将请求响应内容以String方式返回** @param path   请求路径* @param method 请求方法* @param body   请求数据体* @return 请求响应内容转换成字符串信息*/public static String httpsRequestToString(String path, String method, String body) {if (path == null || method == null) {return null;}String response = null;InputStream inputStream = null;InputStreamReader inputStreamReader = null;BufferedReader bufferedReader = null;HttpsURLConnection conn = null;try {TrustManager[] tm = {new JEEWeiXinX509TrustManager()};SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");sslContext.init(null, tm, new java.security.SecureRandom());SSLSocketFactory ssf = sslContext.getSocketFactory();System.out.println(path);URL url = new URL(path);conn = (HttpsURLConnection) url.openConnection();conn.setSSLSocketFactory(ssf);conn.setDoOutput(true);conn.setDoInput(true);conn.setUseCaches(false);conn.setRequestMethod(method);if (null != body) {OutputStream outputStream = conn.getOutputStream();outputStream.write(body.getBytes("UTF-8"));outputStream.close();}inputStream = conn.getInputStream();inputStreamReader = new InputStreamReader(inputStream, "UTF-8");bufferedReader = new BufferedReader(inputStreamReader);String str = null;StringBuffer buffer = new StringBuffer();while ((str = bufferedReader.readLine()) != null) {buffer.append(str);}response = buffer.toString();} catch (Exception e) {} finally {if (conn != null) {conn.disconnect();}try {bufferedReader.close();inputStreamReader.close();inputStream.close();} catch (IOException execption) {}}return response;}//    public static void main(String[] args) throws Exception{//        //媒体文件路径
//        String filePath = "D:/我.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());
//
//    }
}class JEEWeiXinX509TrustManager implements X509TrustManager {public void checkClientTrusted(X509Certificate[] chain, String authType)throws CertificateException {}public void checkServerTrusted(X509Certificate[] chain, String authType)throws CertificateException {}public X509Certificate[] getAcceptedIssuers() {return null;}
}

TokenUtil:

/** 微信公众平台(JAVA) SDK** Copyright (c) 2014, Ansitech Network Technology Co.,Ltd All rights reserved.* * http://www.weixin4j.org/** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at**      http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/
package org.weixin4j.util;import org.weixin4j.Configuration;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;/*** <p>* Title: 微信公众平台Token算法工具类</p>** <p>* Description: 为应用提供URL算法 根据不同的URL返回不同的Token,以适应多微站的需求* 例如:Url:http://www.weixin4j.org/api/tiexinqiao* 则默认Token:为jEvQdLxi0PvtgK8N+HzUpA== 根据配置的系统Token不同,而改变</p>** @author yangqisheng* @since 0.0.1*/
public class TokenUtil {//此加密密钥用于加密公众号Token,一经配置,不能修改,一旦修改,所有公众号需要重新填写Tokenprivate static String systemToken = null;/*** 获取配置文件配置的Token** @return 微站Token*/public static String get() {if (systemToken == null) {systemToken = Configuration.getProperty("weixin4j.token", "weixin4j");}return systemToken;}/*** 加密/校验流程如下:** <p>* 1. 将token、timestamp、nonce三个参数进行字典序排序<br>* 2.将三个参数字符串拼接成一个字符串进行sha1加密<br>* 3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信<br>* </p>** @param token Token验证密钥* @param signature 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数,nonce参数* @param timestamp 时间戳* @param nonce 随机数* @return 验证成功返回true,否则返回false*/public static boolean checkSignature(String token, String signature, String timestamp, String nonce) {List<String> params = new ArrayList<String>();params.add(token);params.add(timestamp);params.add(nonce);//1. 将token、timestamp、nonce三个参数进行字典序排序Collections.sort(params, new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {return o1.compareTo(o2);}});//2. 将三个参数字符串拼接成一个字符串进行sha1加密String temp = SHA1.encode(params.get(0) + params.get(1) + params.get(2));//3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信return temp.equals(signature);}
}

还有上面刚刚发的WeChatUtil。

用到的VO:

Music:

package com.zhongyitang.vehicle.common.weixin.vo;import lombok.Data;/*** 音乐消息* By Oliver_Deng*/
@Data
public class Music {public String title;public String description;public String musicUrl;public String hqMusicUrl;
}

NewsItem:

package com.zhongyitang.vehicle.common.weixin.vo;import lombok.Data;/*** 图文消息* By Oliver_Deng*/
@Data
public class NewsItem {public String Title;public String Description;public String PicUrl;public String Url;
}

TemplateData:

package com.zhongyitang.vehicle.common.weixin.vo;
/*** 模板数据* @author By Oliver_Deng**/
public class TemplateData {private String value;//模板显示值private String color;//模板显示颜色public String getValue() {return value;}public void setValue(String value) {this.value = value;}public String getColor() {return color;}public void setColor(String color) {this.color = color;}
}

Video:

package com.zhongyitang.vehicle.common.weixin.vo;import lombok.Data;/*** 视频消息* BY Oliver_Deng*/
@Data
public class Video {public String title;public String description;public String mediaId;
}

WxTemplate:

package com.zhongyitang.vehicle.common.weixin.vo;import java.util.Map;/*** 模板基类* @author CLiang**/
public class WxTemplate {private String template_id;//模板IDprivate String touser;//目标客户private String url;//用户点击模板信息的跳转页面private String topcolor;//字体颜色private Map<String,TemplateData> data;//模板里的数据public String getTemplate_id() {return template_id;}public void setTemplate_id(String template_id) {this.template_id = template_id;}public String getTouser() {return touser;}public void setTouser(String touser) {this.touser = touser;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getTopcolor() {return topcolor;}public void setTopcolor(String topcolor) {this.topcolor = topcolor;}public Map<String,TemplateData> getData() {return data;}public void setData(Map<String,TemplateData> data) {this.data = data;}
}

复制完上述代码即可实现模板、图文、文本、音乐、图片推送。

参考自博客:https://blog.csdn.net/u011752195/article/details/81675818

微信公众号开发-----实现模板、图文、文本、音乐、图片推送相关推荐

  1. C#微信公众号开发系列教程五(接收事件推送与消息排重)

    C#微信公众号开发系列教程五(接收事件推送与消息排重) 原文:C#微信公众号开发系列教程五(接收事件推送与消息排重) 微信公众号开发系列教程一(调试环境部署) 微信公众号开发系列教程一(调试环境部署续 ...

  2. 微信公众号开发,扫描二维码事件推送丢失参数问题

    需求场景: 每个用户都有自己的推广二维码,分享自己推广二维码,别人扫码后跳转关注公众号,关注后绑定推荐关系. 用到技术点: 1.生成带参数二维码 2.接收事件推送,这里主要是关注和扫带参数二维码两个事 ...

  3. 微信公众号开发(2)——文本消息、图文消息发送

    接上一讲,微信公众号开发(1)--服务器配置 新建几个信息类: package com.imooc.po;/*** 信息基类* @author lyj**/ public class BaseMess ...

  4. 微信公众号开发上传图文素材带有卡片小程序报错:errcode=45166,errmsg = invalid content hint...

    微信公众号开发自从支持允许在群发图文中插入小程序,方便了小程序的运营及推广.最近在三方服务开发中,要支持图文素材插入小程序遇到了一个很是棘手的问题.官方给出的插入小程序的示例支持文字.图片.卡片.如下 ...

  5. 微信公众号开发之上传图文消息素材(十二)

    群发消息太重要了,公众号管理员需要定期通过公众号群发一些消息,用户通过推送的消息可以定期了解公众号的最新信息. 群发图文消息的过程如下: 首先,预先将图文消息中需要用到的图片,使用上传图文消息内图片接 ...

  6. java上传图文消息_微信公众号开发之上传图文消息素材(十二)

    群发消息太重要了,公众号管理员需要定期通过公众号群发一些消息,用户通过推送的消息可以定期了解公众号的最新信息. 群发图文消息的过程如下: 首先,预先将图文消息中需要用到的图片,使用上传图文消息内图片接 ...

  7. 微信公众号开发 [04] 模板消息功能的开发

    1.模板消息的概况 模板消息的定位是用户触发后的通知消息,不允许在用户没做任何操作或未经用户同意接收的前提下,主动下发消息给用户.目前在特殊情况下允许主动下发的消息只有故障类和灾害警示警告类通知,除此 ...

  8. 微信公众号开发笔记(八)发送图片消息

    发送图片消息 上一篇我们已经上传一个图片素材,并且微信服务器也返回给我们一个media_id,接下来我们使用media_id来构造图片消息 代码实现功能 在Message.Util工具类添加代码 1. ...

  9. 公众号 接收规则 消息_微信公众号开发之模板消息

    欢迎留言.转发 微信极速开发系列文章:点击这里 最近有点小感冒,文章的更新进度延误了一些,希望此系列文章对你研究微信公众开发有帮助.前几篇文章介绍了微信支付. 公众号支付.微信扫码支付.刷卡支付.微信 ...

最新文章

  1. 选择性模糊及其算法的实现。
  2. cvRemap 对图像进行普通几何变换
  3. CS184.1X 计算机图形学导论 第8讲 学习笔记
  4. android 版本更新
  5. 0-1背包问题(多解)
  6. rac部署过程的任务列表
  7. 详述怎么使用Linux救援模式
  8. I2C 总线详解-转
  9. Rational rose软件安装遇到key错误的问题
  10. 薛定谔 Maestro教程--用户界面 | 结构编辑 | 测量距离角度
  11. 【神经网络】学习笔记五—循环神经网络RNN简介1.0
  12. Android 网页无法打开 net:ERR_UNKNOWN_URL_SCHEME
  13. 2238. Number of Times a Driver Was a Passenger
  14. 华为的海外员工生活写实
  15. C++学习日记#2——幂法求矩阵的主特征值
  16. LeetCode20.有效的括号——纯C
  17. vue根据银行账号识别银行卡信息
  18. C++设计模式之二(设计模式六大原则)
  19. vue之双向绑定与计算属性
  20. Sygate Personal Firewall 5.6

热门文章

  1. 从Hadoop到Spark、Flink,大数据处理框架十年激荡发展史
  2. NewTek LightWave 3D 2018 破解版
  3. Hive alter column
  4. 通过uart串口和printf函数打印
  5. 手机突然提示无服务,无法使用蜂窝移动 解决流程
  6. 算法设计与分析:多重背包问题
  7. 下血本买的!1-3年的Android开发工程师看过来,挥泪整理面经
  8. 友价源码开发系统如何对接验证码功能?
  9. MAC快速查看本地 SSH KEY
  10. java 最后的异常_关于java:异常处理尝试没有catch,但最后