一、注册公众号

1、公众号介绍

微信公众号分为服务号、订阅号、企业号;订阅号可以个人申请,服务号和企业号要有企业资质才可以。所以我们这里说的公众号开发指的是订阅号,订阅号每天可群发1条消息;

2、注册微信公众号

注册微信公众号
与注册其他网站大同小异,需要注意的一点是,到了下边这步时选择"订阅号",其他不再详细介绍了(因为太简单了)

注册后在微信订阅号里就可以搜索到自己的公众号了

3、注册测试公众号

对于开发者而言,个人订阅号有很多接口是没有权限,也就是说个人订阅号无法使用一些高级的权限接口,如生成二维码、网页授权、自定义菜单、微信支付等,但是,为了方便开发者学习,微信公众平台提供了测试公众账号,测试公众号有很多个人订阅号不具备的权限, 测试公众号的注册地址为:
测试公众号注册
注册步骤略过(太简单),注册后得到"测试号信息"如下

4、内外网穿透

开发基于微信公众号的应用最大的痛苦之处就是调试问题,每次实现一个功能后都需要部署到一个公网服务器进行测试,因为微信用户每次向公众号发起请求时,微信服务器会先接收到用户的请求,然后再转发到我们的服务器上,也就是说,微信服务器是要和我们的服务器进行网络交互,所以我们必须保证我们的服务器外网可以访问到,这种部署到公网服务器进行测试的做法对于我们开发者来说简直是噩梦。所以我们要想一个办法可以做到本地部署、本地调试代码,而要做到这一点,那么我们要解决的问题就是将内网的部署服务器映射到外网,让微信服务器可以正常访问到,幸运的是,借助于第三方软件Ngrok,我们就可以做得到。Ngrok是一个免费的软件Ngrok,使用Ngrok后,我们就可以实现内网穿透,也就是说我们可以将内网的服务器映射到外网给别人访问,这对于我们在本地开发环境中调试微信代码是以及给用户演示一些东西非常快速和有帮助的,因为可以直接使用我们自己的内网的电脑作为服务器,不过Ngrok需要翻墙访问.
不会翻墙的同学在下边做评论,可以获取翻墙资料;
当然,国内也提供了不需要翻墙的技术natapp官网
进入官网主要目的是获取token,步骤如下:

需要注册账号来获取属于自己的token:



我们的目的就是获取这个token

配置环境变量path:

双击natapp.exe,输入以下地址:

natapp -authtoken  yourtoken


回车:

外网访问上边Forwarding后边的那个地址,就能访问到我们本地的地址了;
至此,内外网穿透完成,后边可以调试我们的公众号代码了;

5、微信公众号接入

官方介绍的步骤
按这里的步骤执行即可,非常简单

官方文档不明白的可以看下边的步骤
1、登录自己的公众号后台,选择 开发–基本配置

2、选择 同意 后,点击 成为开发者

3、启用开发者密码

4、点击修改配置

填写下边的信息,写完后点击 提交

提交时会有下边这些错误:
“系统发生错误,请稍后重试”、“token验证失败”、“请求URL超时”,解决办法看这里
公众号配置时遇到的问题及解决办法

进入测试号里测试公众号,填写下边的信息后点击提交

提交后向下拉取页面会看到属于自己的测试二维码,扫描关注,这个测试公众号就是你的测试账号了,在这个公众号里发消息的话就会进入我们的后台代码里

至此,我们的微信公众号已经可以和我们的本地服务器(本地代码)进行交互了;

6、access_token介绍和获取

1、简介

access_token是公众号的全局唯一接口调用凭据,在使用微信公众号各个接口中都需要一个各自的access_token;我们开发人员对这个值需要进行妥善保存;access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时(因为access_token有2个小时的时效性,所以要有一个机制保证最长2个小时重新获取一次),需定时刷新,重复获取将导致上次获取的access_token失效;且所有接口调用每天限制2000次,所以调用接口不能太频繁;

2、获取access_token

根据AppID和AppSecret可获取access_token(顺便将自己的本地服务器ip添加到下边的白名单里)

然后访问该地址,即可调用接口了

https请求方式: GET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

调用结果说明:

返回40125:官网没有这个错误码的介绍

解决40125错误码:重置AppSecret即可

最后返回正确结果:得到了我们想要的access_token值(后边的7200指的是这个access_token有效时间为7200秒,大约1.944小时)

3、代码里获取access_token

AccessToken 实体类

/*** Author: sgw* Date 2019/4/5* Description:保存微信返回的access_token值**/
public class AccessToken {/*** 获取到的凭证*/private String tokenName;/*** 凭证有效时间  单位:秒*/private int expireSecond;public String getTokenName() {return tokenName;}public void setTokenName(String tokenName) {this.tokenName = tokenName;}public int getExpireSecond() {return expireSecond;}public void setExpireSecond(int expireSecond) {this.expireSecond = expireSecond;}
}

AccessToken 封装类

/*** Author: sgw* Date 2019/4/5* Description:封装微信返回的access_token值**/
public class AccessTokenInfo {public static AccessToken accessToken = null;
}

向微信发送请求的工具类

package com.example.maltose.wexin.utils;
import javax.net.ssl.*;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
/*** Author: sgw* Date 2019/4/5* Description:用于发送http请求的工具类(向微信发送http请求,获取access_token)**/
public class NetWorkUtil {/*** 发起HTTPS请求* @param reqUrl* @param requestMethod 请求方式,传null的话默认是get请求* @return 相应字符串*/public String getHttpsResponse(String reqUrl, String requestMethod) {URL url;InputStream is;String result ="";try {url = new URL(reqUrl);HttpsURLConnection con = (HttpsURLConnection) url.openConnection();TrustManager[] tm = {xtm};SSLContext ctx = SSLContext.getInstance("TLS");ctx.init(null, tm, null);con.setSSLSocketFactory(ctx.getSocketFactory());con.setHostnameVerifier(new HostnameVerifier() {@Overridepublic boolean verify(String arg0, SSLSession arg1) {return true;}});//允许输入流,即允许下载con.setDoInput(true);//在android中必须将此项设置为false,允许输出流,即允许上传con.setDoOutput(false);//不使用缓冲con.setUseCaches(false);if (null != requestMethod && !requestMethod.equals("")) {//使用指定的方式con.setRequestMethod(requestMethod);} else {//使用get请求con.setRequestMethod("GET");}//获取输入流,此时才真正建立链接is = con.getInputStream();InputStreamReader isr = new InputStreamReader(is);BufferedReader bufferReader = new BufferedReader(isr);String inputLine;while ((inputLine = bufferReader.readLine()) != null) {result += inputLine + "\n";}System.out.println(result);} catch (Exception e) {e.printStackTrace();}return result;}X509TrustManager xtm = new X509TrustManager() {@Overridepublic X509Certificate[] getAcceptedIssuers() {return null;}@Overridepublic void checkServerTrusted(X509Certificate[] arg0, String arg1)throws CertificateException {}@Overridepublic void checkClientTrusted(X509Certificate[] arg0, String arg1)throws CertificateException {}};
}

随系统的启动就启动的类,即系统启动的时候就去获取access_token

package com.example.maltose.wexin.service;import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.example.maltose.wexin.domain.AccessToken;
import com.example.maltose.wexin.domain.AccessTokenInfo;
import com.example.maltose.wexin.utils.NetWorkUtil;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;/*** Author: sgw* Date 2019/4/5* Description: 默认启动项目的时候就启动该类,用来向微信后台定期获取access_token值* 继承ApplicationRunner接口的话,项目启动时就会执行里边的run方法**///@Order定义组件加载顺序
@Order(value = 1)
@Component
public class StartService implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) throws Exception {System.out.println("开始获取微信里的access_token");/*final String appId = getInitParameter("appId");final String appSecret = getInitParameter("appSecret");*/final String appId = "wx940842767f562278";final String appSecret = "018b2ffb9141c926aedebcd5a23c718f";//获取accessTokenAccessTokenInfo.accessToken = getAccessToken(appId, appSecret);}private AccessToken getAccessToken(String appId, String appSecret) {NetWorkUtil netHelper = new NetWorkUtil();/*** 接口地址为https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET,其中grant_type固定写为client_credential即可。*/String Url = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", appId, appSecret);//此请求为https的get请求,返回的数据格式为{"access_token":"ACCESS_TOKEN","expires_in":7200}String result = netHelper.getHttpsResponse(Url, "");System.out.println("获取到的access_token="+result);//使用FastJson将Json字符串解析成Json对象JSONObject jsStr = JSONObject.parseObject(result);JSONObject json = JSON.parseObject(result);AccessToken token = new AccessToken();token.setTokenName(json.getString("access_token"));token.setExpireSecond(json.getInteger("expires_in"));return token;}
}

上边的JSONObject 需要使用阿里的fastJson依赖:

<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.4</version>
</dependency>

至此,项目需要的 access_token已经成功获取;

小总结:
当我们电脑关机后,第二天继续开发的话,需要做的前几步工作
1、通过natapp开启本地外网访问的权限

2、微信公众号测试管理平台与微信后台基本配置里, 修改接口配置信息URL为: natapp生成的新域名/本地项目地址 , 这时就可以进行测试了

3、启动本地项目,启动项目的同时获取到了access_token值

4、进入微信测试公众号手机端,发送消息就可以在我们的后台接收到消息了

7、被动发送消息

1、介绍

被动发送消息:即当用户在手机公众号里发送消息的时候,我们要被动的做出响应,就是大家在公众号里发送关键词的时候会得到一些固定的回复;

2、写工具类获取公众号发来的信息并作出响应

package com.example.maltose.wechat.utils;import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
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;/*** @Author: sgw* @Date 2019/4/6* @Description:处理公众号发来的消息(XML格式) 将解析结果存储在HashMap中**/
public class MessageHandlerUtils {private static Logger logger = LoggerFactory.getLogger(MessageHandlerUtils.class);/*** 获取微信公众号里发送过来的消息* @param request* @param response* @return*/public static Map<String,String> getMsgFromClient(HttpServletRequest request){logger.info("获取输入流,开始处理消息");// 将解析结果存储在HashMap中Map<String,String> map = new HashMap();InputStream inputStream=null;try {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) {logger.info(e.getName() + "|" + e.getText());map.put(e.getName(), e.getText());}} catch (Exception e) {e.printStackTrace();}finally {// 释放资源try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}return map;}/*** 根据消息类型 构造返回消息*/public static String buildXml(Map<String,String> map) {String result;String msgType = map.get("MsgType").toString();logger.info("消息类型:"+map.get("MsgType").toString());if(msgType.toUpperCase().equals("TEXT")){result = buildTextMessage(map, "来了老弟?");}else{String fromUserName = map.get("FromUserName");// 开发者微信号String toUserName = map.get("ToUserName");result = 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, getUtcTime(),"请回复如下关键词:\n文本\n图片\n语音\n视频\n音乐\n图文");}return result;}/*** 构造文本消息* @param map* @param content* @return*/private static String buildTextMessage(Map<String,String> map, String content) {//发送方帐号String fromUserName = map.get("FromUserName");// 开发者微信号String toUserName = map.get("ToUserName");/*** 文本消息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, getUtcTime(), content);}/*** 获取当前时间* @return*/private static String getUtcTime() {// 如果不需要格式,可直接用dt,dt就是当前系统时间Date dt = new Date();// 设置显示格式DateFormat df = new SimpleDateFormat("yyyyMMddhhmm");String nowTime = df.format(dt);long dd = (long) 0;try {dd = df.parse(nowTime).getTime();} catch (Exception e) {}logger.info("当前时间:"+String.valueOf(dd));return String.valueOf(dd);}
}

3、在入口类里引入上边的工具库

      try {request.setCharacterEncoding("UTF-8");response.setCharacterEncoding("UTF-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}Map<String, String> map = MessageHandlerUtils.getMsgFromClient(request);System.out.println("开始构造消息");String result = "";result = MessageHandlerUtils.buildXml(map);if (result.equals("")) {result = "未正确响应";}try {response.getWriter().write(result);} catch (IOException e) {e.printStackTrace();}

完整的入口类:

package com.example.maltose.wechat.controller;import com.example.maltose.wechat.utils.FileTypeJudge;
import com.example.maltose.wechat.utils.MessageHandlerUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @Author: sgw* @Date 2019/4/5* @Description: 公众号入口**/
@RequestMapping("gzh")
@Controller
public class GzhController {/*** 切记:这里是自定义的token,需和你微信配置界面提交的token完全一致*/private final String TOKEN = "sgwishandsome";static Logger logger = LoggerFactory.getLogger(GzhController.class);@RequestMapping("testone")public void checkSignature(HttpServletRequest request, HttpServletResponse response) {logger.info("校验签名start");/*** 接收微信服务器发送请求时传递过来的参数*///签名String signature = request.getParameter("signature");//时间戳String timestamp = request.getParameter("timestamp");//随机数String nonce = request.getParameter("nonce");//随机字符串String echostr = request.getParameter("echostr");String method = request.getMethod();if(method.equals("GET")){//get请求,说明是在配置微信后台的url过来的请求/*** 将token、timestamp、nonce三个参数进行字典序排序* 并拼接为一个字符串*/String sortStr = this.sort(TOKEN, timestamp, nonce);/*** 对排序后的sortStr进行shal加密*/String mySignature = shal(sortStr);/*** 校验"微信服务器传递过来的签名"和"加密后的字符串"是否一致, 如果一致则签名通过,否则不通过* 每次刚启动项目后,把下边的注释打开,与微信基本配置里的URL进行交互* 配置完毕后把下边代码注释掉即可*/if (!"".equals(signature) && !"".equals(mySignature) && signature.equals(mySignature)) {logger.info("签名校验通过");try {//必须响应给微信,不然会提示"token校验失败"if(echostr!=null&&echostr!=""){response.getWriter().write(echostr);}} catch (IOException e) {e.printStackTrace();}} else {logger.info("校验签名失败");}}else{//post请求,说明是微信公众号里来的请求try {request.setCharacterEncoding("UTF-8");response.setCharacterEncoding("UTF-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}Map<String, String> map = MessageHandlerUtils.getMsgFromClient(request);System.out.println("开始构造消息");String result = "";result = MessageHandlerUtils.buildXml(map);if (result.equals("")) {result = "未正确响应";}try {response.getWriter().write(result);} catch (IOException e) {e.printStackTrace();}}//开始解析解析公众号里发来的消息 将解析结果存储在HashMap中}/*** 参数排序** @param token* @param timestamp* @param nonce* @return*/public String sort(String token, String timestamp, String nonce) {String[] strArray = {token, timestamp, nonce};Arrays.sort(strArray);StringBuilder sb = new StringBuilder();for (String str : strArray) {sb.append(str);}return sb.toString();}/*** 字符串进行shal加密** @param str* @return*/public String shal(String str) {try {MessageDigest digest = MessageDigest.getInstance("SHA-1");digest.update(str.getBytes());byte messageDigest[] = digest.digest();StringBuffer hexString = new StringBuffer();// 字节数组转换为 十六进制 数for (int i = 0; i < messageDigest.length; i++) {String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);if (shaHex.length() < 2) {hexString.append(0);}hexString.append(shaHex);}return hexString.toString();} catch (NoSuchAlgorithmException e) {e.printStackTrace();}return "";}
}

如果是首次关注自己的测试公众号,关注的同时会收到这样一条消息:

在该公众号里发消息会收到如下信息:

如果第一次关注,后台收到的消息:MsgType为event

ToUserName|gh_44c20ba87500
FromUserName|o6jwg1JwBn48Y6I601axmJKDnsvc
CreateTime|1554532821
MsgType|event
Event|unsubscribe

如果关注后,发送文本消息的话,后台收到的消息:MsgType为text

ToUserName|gh_44c20ba87500
FromUserName|o6jwg1JwBn48Y6I601axmJKDnsvc
CreateTime|1554532840MsgType|text
Content|你好啊
MsgId|22255558984717693

如果发图片的话,后台收到的信息:MsgType为image

ToUserName|gh_44c20ba87500
FromUserName|o6jwg1JwBn48Y6I601axmJKDnsvc
CreateTime|1554538246
MsgType|image

如果发语音的话,后台收到的信息:MsgType为voice

ToUserName|gh_44c20ba87500
FromUserName|o6jwg1JwBn48Y6I601axmJKDnsvc
CreateTime|1554533120
MsgType|voice
Format|amr
MsgId|22255564860469296
Recognition|

4、被动返回图片给用户

1、上传图片的工具类

package com.example.maltose.wechat.utils;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.multipart.FilePart;
import org.apache.commons.httpclient.methods.multipart.MultipartRequestEntity;
import org.apache.commons.httpclient.methods.multipart.Part;
import org.apache.commons.httpclient.methods.multipart.StringPart;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.SSLProtocolSocketFactory;
import org.apache.commons.httpclient.util.HttpURLConnection;import java.io.*;
import java.net.URL;/*** @Author: sgw* Date 2019/4/6* Description:上传图片、语音、视频的工具类,用来获取微信返回的mediaId**/
public class UploadMediaApiUtils {/*** 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)URL*/private 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 String getAccessToken(String appId, String appSecret) {NetWorkUtil netHelper = new NetWorkUtil();String Url = String.format("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s", appId, appSecret);String result = netHelper.getHttpsResponse(Url, "");JSONObject json = JSON.parseObject(result);return json.getString("access_token");}/*** 素材上传到微信服务器* @param file  File file = new File(filePath); // 获取本地文件* @param token access_token* @param type type只支持四种类型素材(video/image/voice/thumb)* @return*/public  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;}JSONObject jsonObject = null;PostMethod post = new PostMethod(UPLOAD_MEDIA);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;}public static File downloadMedia(String fileName, String token, String mediaId) {String path = getDownloadUrl(token, mediaId);//return httpRequestToFile(fileName, url, "GET", null);if (fileName == null || path == 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("GET");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();}try {inputStream.close();fileOut.close();} catch (IOException execption) {}}return file;}
}

2、上传图片到微信服务器

    @RequestMapping("upload")public void uploadImg(HttpServletRequest request, HttpServletResponse response) {UploadMediaApiUtils uploadMediaApiUtil = new UploadMediaApiUtils();//这里写成大家自己的appID与appSecret,不要写我这个哈String appId = "wx94084342767f342562278";String appSecret = "018b2ffb9141c926547gebcd5a23c18f";String accessToken = uploadMediaApiUtil.getAccessToken(appId,appSecret);String filePath = "F:aa.jpg";File file = new File(filePath);String type = "IMAGE";JSONObject jsonObject = uploadMediaApiUtil.uploadMedia(file,accessToken,type);System.out.println("json值:"+jsonObject.toString());}

上传成功后微信会返回我们需要的media_id:

在消息处理类MessageHandlerUtils.java里编写返回图片的逻辑

/*** 返回图片给用户* @param map* @return*/private static String buildImageMessage(Map<String, String> map) {String fromUserName = map.get("FromUserName");String toUserName = map.get("ToUserName");/*返回指定的图片,这个media_id就是上传的时候返回的值,这里如果要给用户返回固定的图片就按下边的这样写就行//String media_id = "4wAKxILRJ0tM2gyHT38xOTzjJeLz8AoJQapuEzh2tgcWWpeAJZ3aMZwKo7ULIg";/*返回用户发过来的图片*/String media_id = map.get("MediaId");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, getUtcTime(), media_id);}

返回音频给用户

 /*** 返回语音消息给公众号* @param map* @return iPB5f0tt78B8fKME0gN48eSgfZ4pDt8Y-8HxNd8SmVg4NoHqxux3tnThsh8rVn9h*/private static String sendVoiceMessage(Map<String, String> map) {String fromUserName = map.get("FromUserName");String toUserName = map.get("ToUserName");/*返回用户发过来的语音*/// String media_id = map.get("MediaId");String media_id = "1jmI3WfxvrWHwiX8Y4h8XvcWvMhxhgiFClo7Z_KBAZMgclAU8gRwQuKfKVNkVGfd";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, getUtcTime(),media_id);}

返回视频给用户

 /*** 返回视频消息* @param map* @return*/private static String sendVideoMessage(Map<String, String> map) {String fromUserName = map.get("FromUserName");String toUserName = map.get("ToUserName");String title = "老弟发来视频了哈";String description = "小老弟挺能玩儿呀";//返回用户发过来的视频,这种方式目前这样写不支持,后续再研究//String media_id = map.get("MediaId");String media_id = "D9ii_yDGFrKrkcf_VBoTOzC7pgDIGmuKOnMYYJbtW0S4JSIta5kS0LShBcxNy_k2";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, getUtcTime(),media_id,title,description);}

返回在线音乐给用户:

 /*** 返回音乐消息* 微信接收不到公众号传来的MP3文件,提示不支持该类型* 但是我们可以返回MP3格式的文件给客户* @param map* @return*/private static String sendMusicMessage(Map<String, String> map) {String fromUserName = map.get("FromUserName");String toUserName = map.get("ToUserName");String title = "music";String description = "good music";String hqMusicUrl ="http://www.kugou.com/song/20qzz4f.html?frombaidu#hash=20C16B9CCCCF851D1D23AF52DD963986&album_id=0";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>"+ //非必须项 高质量音乐链接,WIFI环境优先使用该链接播放音乐"</Music>" +"</xml>",fromUserName,toUserName, getUtcTime(),title,description,hqMusicUrl,hqMusicUrl);}

返回图文消息(网页链接)

 /*** 返回图文消息* @param map* @return*/private static String buildNewsMessage(Map<String, String> map) {String fromUserName = map.get("FromUserName");String toUserName = map.get("ToUserName");String title1 = "centos7.0下安装配置redis5.0的详细步骤";String description1 = "redis最新发布的5.0版本,变化较大,这里做一下安装配置的最新总结";String picUrl1 ="https://img-blog.csdnimg.cn/20190331101636249.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzNDE3MzIx,size_16,color_FFFFFF,t_70";String textUrl1 = "https://blog.csdn.net/qq_33417321/article/details/88924934";String title2 = "SpringBoot从入门到放弃";String description2 = "SpringBoot与SpringCloud已经非常成熟了,使用率也在逐年攀升";String picUrl2 ="https://img-blog.csdn.net/20181019204349772?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzMzNDE3MzIx/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70";String textUrl2 = "https://blog.csdn.net/qq_33417321/article/details/83098210";return String.format("<xml>" +"<ToUserName><![CDATA[%s]]></ToUserName>" +"<FromUserName><![CDATA[%s]]></FromUserName>" +"<CreateTime>%s</CreateTime>" +"<MsgType><![CDATA[news]]></MsgType>" +"<ArticleCount>2</ArticleCount>" + //图文消息个数,限制为8条以内"<Articles>" + //多条图文消息信息,默认第一个item为大图,注意,如果图文数超过8,则将会无响应"<item>" +"<Title><![CDATA[%s]]></Title> " +"<Description><![CDATA[%s]]></Description>" +"<PicUrl><![CDATA[%s]]></PicUrl>" + //图片链接,支持JPG、PNG格式,较好的效果为大图360*200,小图200*200"<Url><![CDATA[%s]]></Url>" + //点击图文消息跳转链接"</item>" +"<item>" +"<Title><![CDATA[%s]]></Title>" +"<Description><![CDATA[%s]]></Description>" +"<PicUrl><![CDATA[%s]]]></PicUrl>" +"<Url><![CDATA[%s]]]></Url>" +"</item>" +"</Articles>" +"</xml>",fromUserName,toUserName, getUtcTime(),title1,description1,picUrl1,textUrl1,title2,description2,picUrl2,textUrl2);}

上传音频、视频、图片的代码

 @RequestMapping("upload")public void uploadImg(HttpServletRequest request, HttpServletResponse response) {UploadMediaApiUtils uploadMediaApiUtil = new UploadMediaApiUtils();String appId = "wxa41292f8f201fac3";String appSecret = "f8ad19d4d51d973d6a065a8a4796d281";String accessToken = uploadMediaApiUtil.getAccessToken(appId,appSecret);//上传图片/*String filePath = "F:aa.jpg";String type = "IMAGE";*///上传视频/* String filePath = "F:bb.mp4";String type = "VIDEO";*///上传音频String filePath = "F:gg.mp3";String type = "VOICE";File file = new File(filePath);JSONObject jsonObject = uploadMediaApiUtil.uploadMedia(file,accessToken,type);System.out.println("json值:"+jsonObject.toString());}

返回emoji表情:

package com.example.maltose.wechat.utils;import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/*** Author: sgw* Date 2019/4/6* Description:表情处理**/
public class EmojiUtils {/*** 显示不可见字符的Unicode** @param input* @return*/public static String escapeUnicode(String input) {StringBuilder sb = new StringBuilder(input.length());@SuppressWarnings("resource")Formatter format = new Formatter(sb);for (char c : input.toCharArray()) {if (c < 128) {sb.append(c);} else {format.format("\\u%04x", (int) c);}}return sb.toString();}/*** 将emoji替换为unicode* @param source* @return*/public  String filterEmoji(String source) {if (source != null) {Pattern emoji = Pattern.compile("[\ue000-\uefff]", Pattern.CASE_INSENSITIVE);Matcher emojiMatcher = emoji.matcher(source);Map<String, String> tmpMap = new HashMap<>();while (emojiMatcher.find()) {String key = emojiMatcher.group();String value = escapeUnicode(emojiMatcher.group());tmpMap.put(key, value);}if (!tmpMap.isEmpty()) {for (Map.Entry<String, String> entry : tmpMap.entrySet()) {String key = entry.getKey().toString();String value = entry.getValue().toString();source = source.replace(key, value);}}}return source;}
}

想要知道下边的第81行,各个表情的字符怎么写,可以使用微信发一个表情到后台,后台获取到的Content值就是表情对应的字符

基于SpringBoot微信公众号的开发相关推荐

  1. 【一篇就够了】springboot微信公众号开发,你的坑我来踩

    [一篇就够了]springboot微信公众号开发,你的坑我来踩 前些日子在抖音上看到一个写给女朋友的微信公众号突然心血来潮自己也想写一个,随后就开始在下面的踩坑填坑的阶段了,因为也是第一次写微信公众号 ...

  2. 视频教程-SpringBoot微信公众号开发-微信开发

    SpringBoot微信公众号开发 就职于国内知名在线互联网旅游公司,10+互联网开发经验,精通前后端开发 刘志强 ¥149.00 立即订阅 扫码下载「CSDN程序员学院APP」,1000+技术好课免 ...

  3. 微信公众号Python开发(Wechatpy+新浪云SAE应用)

    微信公众号Python开发(Wechatpy) 前言:微信公众号后台只提供指定条件的指定回复,如果想有聊天.翻译.查询.后台数据库等则需要使用公众号提供的接口开发脚本.开发工具语言选择诸多,Pytho ...

  4. ios系统web(微信公众号)开发遇到的问题及解决方案

    ios系统web(微信公众号)开发遇到的问题及解决方案 参考文章: (1)ios系统web(微信公众号)开发遇到的问题及解决方案 (2)https://www.cnblogs.com/clj2017/ ...

  5. Yii2.0实现微信公众号后台开发

    2019独角兽企业重金招聘Python工程师标准>>> 研读 微信公众平台开发者文档 ,然后再阅读本文,效果更佳! 接入微信 Yii2后台配置 1.在app/config/param ...

  6. 微信公众号Java开发-笔记02【开发接入准备、开发接入】

    学习视频网址:哔哩哔哩网站 微信公众号开发-Java版 [P01-P02]微信公众号Java开发-笔记01[微信公众号介绍.开发环境搭建] [P03-P04]微信公众号Java开发-笔记02[开发接入 ...

  7. 微信公众号Java开发-笔记01【微信公众号介绍、开发环境搭建】

    学习网址:哔哩哔哩网站 微信公众号开发-Java版 微信公众号Java开发-笔记01[微信公众号介绍.开发环境搭建] 微信公众号Java开发-笔记02[] 微信公众号Java开发-笔记03[] 微信公 ...

  8. 论如何使用Python进行微信公众号的开发

    说到微信公众号的开发,不外乎两种,一种是使用官方提供的接口开发,一种就是通过模拟登录来实现接口(这种方式有被封的风险,这里不论述) Python发展至今,已经有多种完善的服务器框架可以使用,Djang ...

  9. 前端对接微信公众号网页开发流程,前期配置

    微信公众号网页开发,其实就是我们开发的h5网页需要放到微信浏览器环境中使用,但是需要对接公众号授权,授权之后可以获取到用户的个人信息,以及可以使用公众号提供的一些API,如:图片上传.图片预览.获取位 ...

  10. 前端对接微信公众号网页开发流程,授权对接

    前面讲到 前端对接微信公众号网页开发流程,前期配置,本篇文章主要详细介绍关于公众号的授权对接. 一.引入微信js-sdk 在需要调用 JS 接口的页面引入如下 JS 文件 http://res.wx. ...

最新文章

  1. retinaface训练笔记
  2. apache.camel_使用Apache Camel 2.14的轻松REST端点
  3. 使用混合多云每个人都应避免的3个陷阱(第3部分)
  4. 08:vigenère密码_密码技术:Vigenére密码,Playfair密码,Hill密码
  5. Vue Devtools 安装
  6. 路径规划之基于优化的规划算法
  7. asp导出excel文件格式
  8. 视易精通收银服务器自动关机,视易精通量贩式收银系统操作手册3.0
  9. H83601D直插DIP千兆双口网络接口隔离滤波脉冲变压器
  10. 用C#写经理评分系统
  11. “the+形容词”的四种类型及语法特征
  12. java反射--Field用法实践
  13. GoogleVRForUnity✨一款常用的谷歌VR插件教程
  14. 第一篇文献:谈大数据时代的云控制摄影测量 ——张祖勋院士
  15. 现实与理想(中国台湾大学彭明辉)
  16. 注册ActiveX控件简单方法及控件未被正确授权解决方案
  17. Capture CIS 元件库详细分类解析
  18. 关于RedisTemplate的ERR EXEC without MULTI错误
  19. 阿里CEO张勇致股东信:对阿里和中国有信心
  20. uniapp h5 web-view不显示公众号文章

热门文章

  1. Windows安全基础-基线配置
  2. 【一分钟了解UWP】微信UWP
  3. 应试教育——人性的扼杀
  4. 照片视频制作软件哪个好?自动生成酷炫效果,3步快速搞定!
  5. 常用的C语言编程工具
  6. java 重写泛型方法_java – 泛型方法重写8
  7. 跪了,腾讯数据工程师用Python可视化绘制的中国GDP数据地图,大佬就是不一样
  8. Linux设置串口波特率等参数
  9. 教务管理系统乱码服务器不可,青果教务管理系统Post登录(二)
  10. hdu_5145_NPY and girls(莫队算法+组合)