首先贴上官方文档:
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1445241432
私以为这份文档写的还是很不错的,在开发的时候没有给我造成多大的困扰,比微信支付的文档好的不要太多。当然也可能是因为我调用的功能太少,没有碰上坑。
接下来进入正题。


这里会分步介绍代码所实现的功能,在文章最后会附上完整的类以及用到的其他类。

1. 获取AccessToken

access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。

HttpConnection conn = new HttpConnection();StringBuilder sb = new StringBuilder("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential");sb.append("&appid="+PropertiesUtil.getInstance().getPropMap().get("wx.AppKey"));sb.append("&secret="+PropertiesUtil.getInstance().getPropMap().get("wx.AppSecret"));String result = conn.get(sb.toString());System.out.println(result);JSONObject obj = new JSONObject(result);String accessToken = obj.getString("access_token");Instance.ACCESS_TOKEN = accessToken;System.out.println(accessToken);

这里用到了HttpConnection类和PropertiesUtil,在文章最后会贴出该类的完整代码

2. 群发消息

String resp = "";//响应String reqUrl = "https://api.weixin.qq.com/cgi-bin/message/mass/send?access_token="+Instance.ACCESS_TOKEN;try {// 构造httprequest设置HttpClient client = new HttpClient();PostMethod request = new PostMethod(reqUrl);// 添加request headersrequest.addRequestHeader("Content-type", "application/json");request.addRequestHeader("Accept", "application/json");Map<String, Object> param = new HashMap<String, Object>();param.put("touser", openId);param.put("msgtype", "text");Map<String, Object> content =  new HashMap<String, Object>();content.put("content", text);param.put("text",content);String json = new Gson().toJson(param);request.setRequestEntity(new ByteArrayRequestEntity(json.getBytes("UTF-8")));  client.executeMethod(request);  resp = request.getResponseBodyAsString();  System.out.println(resp);} catch (Exception e) {System.out.println("发送POST请求出现异常!" + e);e.printStackTrace();}

该方法参数中的openId为用户在微信的唯一标识,text为需要发送的消息内容。
需要注意的两点,
1.群发接口的openId数组必须包含2个及以上,如果只传一个openid会发送失败,这是我所不能理解的。
2.如果返回code为48003,这个返回码在官方文档中没有说明,出现的原因可能是你的微信公众号没有开通群发功能,需要登录微信公众号,在首页点击“新建群发”按钮,同意之后就可以了。


最后贴上完整的代码,记得把package替换掉:

package com.diecolor.wxpay;import java.util.HashMap;
import java.util.Map;import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.json.JSONObject;import com.diecolor.util.HttpConnection;
import com.diecolor.util.PropertiesUtil;
import com.google.gson.Gson;public class MessageUtil {/*** 内部类* 实例化当前对象**/private static class Instance{private static final MessageUtil mu = new MessageUtil();private static String ACCESS_TOKEN = null;}/*** 暴露在外部的方法* @return*/public static MessageUtil getInstance() {return Instance.mu;}/*** 获取accessToken* @return* @throws Exception */private String getAssessToken() throws Exception {if (Instance.ACCESS_TOKEN==null) {HttpConnection conn = new HttpConnection();StringBuilder sb = new StringBuilder("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential");sb.append("&appid="+PropertiesUtil.getInstance().getPropMap().get("wx.AppKey"));sb.append("&secret="+PropertiesUtil.getInstance().getPropMap().get("wx.AppSecret"));String result = conn.get(sb.toString());System.out.println(result);JSONObject obj = new JSONObject(result);String accessToken = obj.getString("access_token");Instance.ACCESS_TOKEN = accessToken;System.out.println(accessToken);}return Instance.ACCESS_TOKEN;}/*** 群发文本消息* @param openId* @param text* @return*/private boolean messTextMessage(String[] openId,String text) {try {String resp = "";//响应String reqUrl = "https://api.weixin.qq.com/cgi-bin/message/mass/send?access_token="+Instance.ACCESS_TOKEN;try {// 构造httprequest设置HttpClient client = new HttpClient();PostMethod request = new PostMethod(reqUrl);// 添加request headersrequest.addRequestHeader("Content-type", "application/json");request.addRequestHeader("Accept", "application/json");Map<String, Object> param = new HashMap<String, Object>();param.put("touser", openId);param.put("msgtype", "text");Map<String, Object> content =  new HashMap<String, Object>();content.put("content", text);param.put("text",content);String json = new Gson().toJson(param);request.setRequestEntity(new ByteArrayRequestEntity(json.getBytes("UTF-8")));  client.executeMethod(request);  resp = request.getResponseBodyAsString();  System.out.println(resp);} catch (Exception e) {System.out.println("发送POST请求出现异常!" + e);e.printStackTrace();} } catch (Exception e) {e.printStackTrace();}return false;}public static void main(String[] args) throws Exception {MessageUtil.getInstance().getAssessToken();String[] openId = {"oDyjy0pvqxQMV66D7rPzekfxPOUg","oDyjy0hVQSWUU4Np3isCFPy_zC2U"};//MessageUtil.getInstance().messTextMessage(openId, "测试群发消息");}}
package com.diecolor.util;import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;/*** 单例模式读取配置文件* @author 武佳伟丶**/
public class PropertiesUtil {private static Map<String, String> proMap = new HashMap<String,String>();//Maps.newHashMap();//并不知道是干什么用的private static class PropertiesInstance {private static final PropertiesUtil props = new PropertiesUtil();}public static PropertiesUtil getInstance(){return PropertiesInstance.props;}public Map<String,String> getPropMap() {return proMap;}/** 构造函数*/private PropertiesUtil(){proMap = readProperties();}@SuppressWarnings("rawtypes")private static Map<String, String> readProperties() {Properties props = new Properties();InputStream in = null;try {in = PropertiesUtil.class.getClassLoader().getResourceAsStream("upush.properties");props.load(in);Enumeration en = props.propertyNames();while (en.hasMoreElements()) {String key = (String) en.nextElement();String value = props.getProperty(key);proMap.put(key, value);}} catch (Exception e) {e.printStackTrace();} finally {try {in.close();} catch (Exception e2) {e2.printStackTrace();}}return proMap;}
}
package com.diecolor.util;import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.Iterator;
import java.util.Map;import javax.activation.MimetypesFileTypeMap;import org.springframework.stereotype.Component;/*** HTTP通信类**/
@Component
public class HttpConnection {/** * 向指定URL发送GET方法的请求 * @param url 发送请求的URL * @param param 请求参数,请求参数应该是name1=value1&name2=value2的形式。 * @return URL所代表远程资源的响应 */ public  String get(String url, String param) throws Exception {  String urlName = url + "?" + param;  return get(urlName);}  /*** 向指定URL发送GET方法的请求* * @param url*            发送请求的URL* @return URL所代表远程资源的响应*/public  String get(String url) throws Exception {String result = "";BufferedReader in = null;URL realUrl = new URL(url);URLConnection conn = realUrl.openConnection();conn.setRequestProperty("accept", "*/*");conn.setRequestProperty("Accept-Language", "zh-CN,zh;q=0.8");conn.setRequestProperty("Content-Type", "text/xml;charset=utf-8");conn.setRequestProperty("connection", "keep-alive");conn.setRequestProperty("user-agent","Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)");// 建立实际的连接conn.connect();// 定义BufferedReader输入流来读取URL的响应in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));String line;while ((line = in.readLine()) != null) {result += line;}in.close();return result;}/**  * 向指定URL发送POST方法的请求  * @param url 发送请求的URL  * @param content 内容* @return URL所代表远程资源的响应  * @throws Exception */  public String post(String url,String content) throws Exception{String result = "";URL postUrl = new URL(url); HttpURLConnection connection = (HttpURLConnection) postUrl .openConnection(); connection.setDoOutput(true); connection.setDoInput(true); connection.setRequestMethod("POST"); connection.setUseCaches(false); connection.setInstanceFollowRedirects(true); connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); connection.connect(); DataOutputStream out = new DataOutputStream(connection .getOutputStream());
//        out.writeBytes(content); out.write(content.getBytes("UTF-8"));out.flush(); out.close(); // flush and close BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream(),"utf-8"));//设置编码,否则中文乱码 String line=""; while ((line = reader.readLine()) != null){ result += line;} reader.close(); connection.disconnect();return result; }/*** 向指定URL发送POST方法的请求* @Title: post* @Description: TODO* @param @param url* @param @param textMap* @param @return    * @return String    * @throws*/public String post(String url, Map<String, Object> textMap){String res = "";HttpURLConnection conn = null;String BOUNDARY = "---------------------------123821742118716"; //boundary就是request头和上传文件内容的分隔符try {URL postUrl = new URL(url);conn = (HttpURLConnection) postUrl.openConnection();conn.setConnectTimeout(5000);conn.setReadTimeout(30000);conn.setDoOutput(true);conn.setDoInput(true);conn.setUseCaches(false);conn.setRequestMethod("POST");conn.setRequestProperty("Connection", "Keep-Alive");conn.setRequestProperty("User-Agent","Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.6)");conn.setRequestProperty("Content-Type","multipart/form-data; boundary=" + BOUNDARY);OutputStream out = new DataOutputStream(conn.getOutputStream());// textif (textMap != null) {StringBuffer strBuf = new StringBuffer();Iterator iter = textMap.entrySet().iterator();while (iter.hasNext()) {Map.Entry entry = (Map.Entry) iter.next();String inputName = (String) entry.getKey();String inputValue = (String) entry.getValue();if (inputValue == null) {continue;}strBuf.append("\r\n").append("--").append(BOUNDARY).append("\r\n");strBuf.append("Content-Disposition: form-data; name=\""+ inputName + "\"\r\n\r\n");strBuf.append(inputValue);}out.write(strBuf.toString().getBytes());}byte[] endData = ("\r\n--" + BOUNDARY + "--\r\n").getBytes();out.write(endData);out.flush();out.close();// 读取返回数据StringBuffer strBuf = new StringBuffer();BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));String line = null;while ((line = reader.readLine()) != null) {strBuf.append(line).append("\n");}res = strBuf.toString();reader.close();reader = null;} catch (Exception e) {System.out.println("发送POST请求出错。" + url);e.printStackTrace();} finally {if (conn != null) {conn.disconnect();conn = null;}}return res;}/**  * 向指定URL发送POST方法的请求 (带文件) * @param url 发送请求的URL  * @param textMap 文本参数键值* @param fileMap 文件键值* @return URL所代表远程资源的响应  * @throws Exception */  public String filePost(String url, Map<String, String> textMap,Map<String, String> fileMap) {String res = "";HttpURLConnection conn = null;String BOUNDARY = "---------------------------123821742118716"; //boundary就是request头和上传文件内容的分隔符try {URL postUrl = new URL(url);conn = (HttpURLConnection) postUrl.openConnection();conn.setConnectTimeout(5000);conn.setReadTimeout(30000);conn.setDoOutput(true);conn.setDoInput(true);conn.setUseCaches(false);conn.setRequestMethod("POST");conn.setRequestProperty("Connection", "Keep-Alive");conn.setRequestProperty("User-Agent","Mozilla/5.0 (Windows; U; Windows NT 6.1; zh-CN; rv:1.9.2.6)");conn.setRequestProperty("Content-Type","multipart/form-data; boundary=" + BOUNDARY);OutputStream out = new DataOutputStream(conn.getOutputStream());// textif (textMap != null) {StringBuffer strBuf = new StringBuffer();Iterator iter = textMap.entrySet().iterator();while (iter.hasNext()) {Map.Entry entry = (Map.Entry) iter.next();String inputName = (String) entry.getKey();String inputValue = (String) entry.getValue();if (inputValue == null) {continue;}strBuf.append("\r\n").append("--").append(BOUNDARY).append("\r\n");strBuf.append("Content-Disposition: form-data; name=\""+ inputName + "\"\r\n\r\n");strBuf.append(inputValue);}out.write(strBuf.toString().getBytes());}// fileif (fileMap != null) {Iterator iter = fileMap.entrySet().iterator();while (iter.hasNext()) {Map.Entry entry = (Map.Entry) iter.next();String inputName = (String) entry.getKey();String inputValue = (String) entry.getValue();if (inputValue == null) {continue;}File file = new File(inputValue);String filename = file.getName();String contentType = new MimetypesFileTypeMap().getContentType(file);if (filename.endsWith(".png")) {contentType = "image/png";}if (contentType == null || contentType.equals("")) {contentType = "application/octet-stream";}StringBuffer strBuf = new StringBuffer();strBuf.append("\r\n").append("--").append(BOUNDARY).append("\r\n");strBuf.append("Content-Disposition: form-data; name=\""+ inputName + "\"; filename=\"" + filename+ "\"\r\n");strBuf.append("Content-Type:" + contentType + "\r\n\r\n");out.write(strBuf.toString().getBytes());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[] endData = ("\r\n--" + BOUNDARY + "--\r\n").getBytes();out.write(endData);out.flush();out.close();// 读取返回数据StringBuffer strBuf = new StringBuffer();BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));String line = null;while ((line = reader.readLine()) != null) {strBuf.append(line).append("\n");}res = strBuf.toString();reader.close();reader = null;} catch (Exception e) {System.out.println("发送POST请求出错。" + url);e.printStackTrace();} finally {if (conn != null) {conn.disconnect();conn = null;}}return res;}
}

如有疑问,烦请留言。

微信公众平台开发之Java实现群发消息相关推荐

  1. 微信公众平台开发之DIY宣传页

    微信公众平台开发之DIY宣传页功能是商家推广自己的官方微信的途径之一.做任何产品的推广,活动的宣传都离不开用户关注我们的官方微信,所有我们有必要把自己的宣传页做的非常的漂亮,大气和美观,从而吸引用户去 ...

  2. 微信jsapi支付获取code_微信开发之微信公众平台开发之JSAPI公众号支付

    本文将带你了解微信开发微信公众平台开发之JSAPI公众号支付,希望本文对大家学微信有所帮助. 一:配置参数 申请成功后,获取接口文件, 将所有文件放入项目根目录weixin下,在WxPay.ub.co ...

  3. 微信公众平台开发之360全景

     微信公众平台开发之360全景功能是商家推广自己的产品的非常有效的方法之一.现在大多数的人买东西都不喜欢去看繁杂的文字描述,而更喜欢看实物图.所有之前我们做过微信相册用于展示我们的产品,但是光靠相册的 ...

  4. 微信公众平台开发教程Java版(三) 消息接收和发送

    前面两章已经介绍了如何接入微信公众平台,这一章说说消息的接收和发送 可以先了解公众平台的消息api接口(接收消息,发送消息) http://mp.weixin.qq.com/wiki/index.ph ...

  5. java 微信 接收消息_微信公众平台开发教程Java版(三) 消息接收和发送

    https://www.iteye.com/blog/tuposky-2017429 前面两章已经介绍了如何接入微信公众平台,这一章说说消息的接收和发送 可以先了解公众平台的消息api接口(接收消息, ...

  6. 微信公众平台开发之MySql数据库+关键词回复-php语言(三)

    2019-3-28 15:09:02更新: 感谢各位的支持.最早发帖是在大四毕业论文期间,所以针对第一次接触微信公众号的学生.时过境迁,4年过去了,在微信上遇到了方方面面的问题,这里贡献些自己的学习资 ...

  7. Java微信公众平台开发之OAuth2.0网页授权

    根据官方文档点击查看在微信公众号请求用户网页授权之前,开发者需要先到公众平台官网中的"开发 - 接口权限 - 网页服务 - 网页帐号 - 网页授权获取用户基本信息"的配置选项中,修 ...

  8. 微信公众平台开发之LBS等API使用-php语言(六)

    在公众平台开发过程中,有时为了丰富微信公众号,我们需要调用第三方API.下面以BAE3.0 LBS天气查询为例,触类旁通: //==================================== ...

  9. 微信公众平台开发教程Java版(六) 事件处理(菜单点击/关注/取消关注)

    前言: 事件处理是非常重要的,这一章讲讲常见的事件处理 1.关注/取消关注 2.菜单点击 事件类型介绍: 在微信中有事件请求是消息请求中的一种.请求类型为:event 而event事件类型又分多种事件 ...

最新文章

  1. 原理解释|直觉与实现:Batch Normalization
  2. mysql无级分销_3级分销(mysql存储过程写法)
  3. 消息称苹果下代iPhone、iPad产品都将采用钛合金
  4. PHP 学习 一 基础
  5. iPad连android热点掉线,苹果终于承认,iOS 13有这个问题,网络断连的原因找到了...
  6. c++语句switch语句_错误:案例标签不在C中的switch语句内
  7. Apache-Flink深度解析-DataStream-Connectors之Kafka
  8. 工业时序大数据质量管理
  9. 获取客户端ip_获取客户端访问真实IP
  10. 灵活的数据管理和展示javascript类库 - Recline.js
  11. JavaSE学习--单例设计模式
  12. 直播电商源码,无加密
  13. 投资:保险业分析框架
  14. Codeforces469div2F curfew(贪心)
  15. 蓝桥杯算法训练-隐匿的刺客
  16. tensorflow yolov3训练自己的数据集,详细教程
  17. win10上网显示dns服务器未响应,win10无法上网DNS服务器未响应的解决方法
  18. MySQL创建数据表的三种方式
  19. SQL Server数据库的增删改查
  20. 如何不写代码通过爬虫软件采集表格数据

热门文章

  1. 你应该在你的域名中使用www吗?
  2. web html常用标签含义,WEB前端开发之HTML:常用标签知多少
  3. 【前端去重】数组去重字符串去重对象去重
  4. 全志D1-H芯片 如何在tina使用tplayerdemo 进行rtsp拉流说明?
  5. MFC界面控件BCGControlBar v33.3 - 可视化管理器和主题更新
  6. 少了五毛钱不卖的酷炫火焰特效!3D粒子哦!
  7. Oracle 监听端口被占用,别的端口也提示占用
  8. 在知乎逮到一个腾讯10年老测试开发,聊过之后收益良多...
  9. 一级造价工程师(安装)- 计量笔记 - 第四章第二节热力设备工程
  10. 判断单链表成环与否?