java开发微信公众号接受并回复消息[工程代码+图片全解]

写这篇博客时犹豫了好久,因为步骤太多了,上班了也没时间,但是我依然记得当时实现公众号自动回复时的场景,找个案例好

难,也没有一个完整的案例,想了想还是写出来吧,希望能让实现这功能的人少走弯路。

微信公众号平台也有自定义回复消息,比如我在公众号里发送关注你,我们在微信公众号平台设置关键字关注你(就是

有人发送这个关键字就要回复什么内容)设置成回复:**你好,java!**适用于这种固定信息,如果我发送 获取个人信息、我的积

分这种内容就需要动态的数据了,所以要使用我们自己的接口往数据库中进行查询信息。

这个项目实现了简单的回复文字消息,没有 图片、音频等类型的发送、推荐看微信API文档实现,我这也有实现的案例需要的可加我QQ 930496909

首先:

先整理一下大致流程

1.编写java代码,要按照微信公众号提供的API文档来做。

2.下载ngrok工具,假设编写的Java都不会部署到网上(本地运行项目),那我们要想微信能访问我们的java接口我们需要一个工

具(就是将我们本地电脑变成服务器,让别人能来访问我们本地运行的项目说的比较通俗具体可百度~),如果编写的java程序

扫描二维码关注公众号,回复: 12271236 查看本文章

部署到服务器上就不用啦。

3.在微信公众号平台注册服务号或者订阅号[两者的区别在于服务号收钱功能多,其他区别可自行百度~]我注册的是订阅号,然

后在微信平台进行一些配置,(其实就是让关注公众号的人发送消息后能够对接我们的java接口)

4.进行测试。(我会把源码贴到码云上,可以下载源码https://gitee.com/it_qin/weixintest.git)

接下来就是步骤了。

看一下java结构目录

jar包(因为当时在学校做的还不太会用MAVEN管理,无奈啊,jar包主要就是ssm框架jar包和微信的几个包,我把截图发下,上面

我提供的码云链接上也有jar包)
这里写图片描述
下面就是每个文件了

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.0" 
	xmlns="http://java.sun.com/xml/ns/javaee" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
	http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
	 <!--  配置编码(解决中文乱码)过滤器 -->
  <filter>
    <filter-name>encodingFilter</filter-name>
    <filter-class>
	        org.springframework.web.filter.CharacterEncodingFilter
    </filter-class>
    <init-param>
      <param-name>encoding</param-name>
      <param-value>UTF-8</param-value>
    </init-param>
    <init-param>
      <param-name>forceEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
  </filter>
  <filter-mapping>
    <filter-name>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>  </filter-mapping>
<!-- log4j日志文件 -->
  <context-param>
    <param-name>log4jConfigLocation</param-name>
    <param-value>classpath:log4j.properties</param-value>
  </context-param> 
	  <listener>
	    <listener-class>
		org.springframework.web.util.Log4jConfigListener
    </listener-class>
  </listener>
  
  <!-- 创建springMvc的  dispathServlet  -->
  <servlet>
  	<servlet-name>springMvc</servlet-name>
  	<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  	<init-param>
  			<param-name>contextConfigLocation</param-name>
  			<param-value>classpath:springmvc-servlet.xml</param-value>
  	</init-param>
  	<load-on-startup>1</load-on-startup><!-- 服务器一启动就加载 -->
  </servlet>
  <!--拦截后缀是.do-->
  <servlet-mapping>
  	<servlet-name>springMvc</servlet-name>
  	<url-pattern>*.do</url-pattern>
  </servlet-mapping>
  
  <!-- 启动spring容器  spring配置文件的位置-->
  <context-param>
  	<param-name>contextConfigLocation</param-name>
  	<param-value>classpath:applicationContext-mybatis.xml</param-value>
  </context-param> 
  <!-- 监听spring容器的启动 -->
  <listener>
  	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
</web-app>

springmvc-servlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc.xsd">
        
   <context:component-scan base-package="com.qhk.controller"/>
   
   <mvc:annotation-driven>
   		<mvc:message-converters>
   			<bean class="org.springframework.http.converter.StringHttpMessageConverter">
   				<property name="supportedMediaTypes">
   					<list>
   						<value>application/json;charset=UTF-8</value>
   					</list>
   				</property>
   			</bean>
   			<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
   				<property name="supportedMediaTypes">
   					<list>
   						<value>text/html;charset=UTF-8</value>
   						<value>application/json</value>
   					</list>
   				</property>
   				<property name="features">
   					<list>
   						<value>WriteDateUseDateFormat</value>
   					</list>
   				</property>
   			</bean>
   		</mvc:message-converters>
   </mvc:annotation-driven>
    <!-- 配置多视图解析器 -->
    <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="favorParameter" value="true"/> 
        <property name="defaultContentType" value="text/html" /> 
        <property name="mediaTypes">
            <map>
            	<entry key="html" value="text/html; charset=UTF-8"/>
                <entry key="json" value="application/json; charset=UTF-8"/>
                <entry key="xml" value="application/xml; charset=UTF-8"/>
            </map>
        </property>
        <property name="viewResolvers">
            <list>
                <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
				   <property name="prefix" value="/"/>
				   <property name="suffix" value=".jsp"/>
			   </bean>
            </list>
        </property>
    </bean>
 
	<!-- 配置interceptors -->
 	<!-- <mvc:interceptors>
		<mvc:interceptor>
			<mvc:mapping path="/main/**"/>
			<bean class="com.ktv.interceptor.SysInterceptor"/>
		</mvc:interceptor> 
		
	</mvc:interceptors> -->
	 
	<!-- 配置文件上传  MultipartResolver-->
	<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
		<property name="maxUploadSize" value="500000000"/>
		<property name="defaultEncoding" value="UTF-8"/>
	</bean> 
</beans>   

applicationContext-mybatis.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop"
	xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="   
                http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   
                http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd   
                http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd 
                http://www.springframework.org/schema/context 
                http://www.springframework.org/schema/context/spring-context.xsd">

	<context:component-scan base-package="com.qhk.service" />
	<context:annotation-config />
	<context:property-placeholder location="classpath:database.properties" />
	<!-- JNDI获取数据源(使用dbcp连接池) -->
	<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
		destroy-method="close" scope="singleton">
		<property name="driverClassName" value="${driver}" />
		<property name="url" value="${url}" />
		<property name="username" value="${user}" />
		<property name="password" value="${password}" />
		<property name="initialSize" value="${initialSize}" />
		<property name="maxActive" value="${maxActive}" />
		<property name="maxIdle" value="${maxIdle}" />
		<property name="minIdle" value="${minIdle}" />
		<property name="maxWait" value="${maxWait}" />
		<property name="removeAbandoned" value="${removeAbandoned}" />
		<property name="removeAbandonedTimeout" value="${removeAbandonedTimeout}" />
		<property name="testWhileIdle" value="true" />
		<property name="timeBetweenEvictionRunsMillis" value="60000" />
		<property name="testOnBorrow" value="false" />
		<property name="testOnReturn" value="false" />
		<property name="validationQuery" value="select 1" />
		<property name="numTestsPerEvictionRun" value="${maxActive}" />
	</bean>

	<!-- 事务管理 -->
	<bean id="txManager"
		class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource" />
	</bean>

	<!-- 配置mybatis SqlSessionFactoryBean -->
	<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="configLocation" value="classpath:mybatis-config.xml" />
	</bean>

	<aop:aspectj-autoproxy />
	<aop:config proxy-target-class="true">
		<aop:pointcut expression="execution(* *com.qhk.service..*(..))"
			id="transService" />
		<aop:advisor advice-ref="myAdvice" pointcut-ref="transService" />
	</aop:config>

	<tx:advice id="myAdvice" transaction-manager="txManager">
		<tx:attributes>
			<tx:method name="find*" read-only="true" propagation="SUPPORTS" />
			<tx:method name="add*" propagation="REQUIRED" />
			<tx:method name="update*"  propagation="REQUIRED" />
			<tx:method name="del*"  propagation="REQUIRED" />
			<tx:method name="*" />
		</tx:attributes>
	</tx:advice>
	<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"  >
		<property name="basePackage" value="com.qhk.dao" />
	</bean>
	
</beans>

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE configuration   
 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"   
 "http://mybatis.org/dtd/mybatis-3-config.dtd">  
<configuration>  
     <settings>  
         <!-- changes from the defaults -->  
         <setting name="lazyLoadingEnabled" value="false" />  
     </settings>  
     <typeAliases>  
         <!--这里给实体类取别名,方便在mapper配置文件中使用--> 
         <package name="com.qhk.entity"/>
     </typeAliases> 
</configuration>  

database.properties

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/ktvsystem?useUnicode=true&characterEncoding=utf-8
user=root
password=admin
minIdle=45
maxIdle=50
initialSize=5
maxActive=100
maxWait=100
removeAbandonedTimeout=240
removeAbandoned=true

log4j.properties

log4j.rootLogger=debug,CONSOLE,file
#log4j.rootLogger=ERROR,ROLLING_FILE

log4j.logger.cn.smbms=debug
log4j.logger.org.apache.ibatis=debug
log4j.logger.org.mybatis.spring=debug
log4j.logger.java.sql.Connection=debug
log4j.logger.java.sql.Statement=debug
log4j.logger.java.sql.PreparedStatement=debug
log4j.logger.java.sql.ResultSet=debug

######################################################################################
# Console Appender  \u65e5\u5fd7\u5728\u63a7\u5236\u8f93\u51fa\u914d\u7f6e
######################################################################################
log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender
log4j.appender.Threshold=debug
log4j.appender.CONSOLE.DatePattern=yyyy-MM-dd
log4j.appender.CONSOLE.Target=System.out
log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout
log4j.appender.CONSOLE.layout.ConversionPattern= - (%r ms) - %d{yyyy-M-d HH:mm:ss}%x[%5p](%F:%L) %m%n



######################################################################################
# Rolling File  \u6587\u4ef6\u5927\u5c0f\u5230\u8fbe\u6307\u5b9a\u5c3a\u5bf8\u7684\u65f6\u5019\u4ea7\u751f\u4e00\u4e2a\u65b0\u7684\u6587\u4ef6
######################################################################################
#log4j.appender.ROLLING_FILE=org.apache.log4j.RollingFileAppender
#log4j.appender.ROLLING_FILE.Threshold=INFO
#log4j.appender.ROLLING_FILE.File=${baojia.root}/logs/log.log
#log4j.appender.ROLLING_FILE.Append=true
#log4j.appender.ROLLING_FILE.MaxFileSize=5000KB
#log4j.appender.ROLLING_FILE.MaxBackupIndex=100
#log4j.appender.ROLLING_FILE.layout=org.apache.log4j.PatternLayout
#log4j.appender.ROLLING_FILE.layout.ConversionPattern=%d{yyyy-M-d HH:mm:ss}%x[%5p](%F:%L) %m%n

######################################################################################
# DailyRolling File  \u6bcf\u5929\u4ea7\u751f\u4e00\u4e2a\u65e5\u5fd7\u6587\u4ef6\uff0c\u6587\u4ef6\u540d\u683c\u5f0f:log2009-09-11
######################################################################################
log4j.appender.file=org.apache.log4j.DailyRollingFileAppender
log4j.appender.file.DatePattern=yyyy-MM-dd
log4j.appender.file.File=${AppInfoSystem.root}/logs/log.log
log4j.appender.file.Append=true
log4j.appender.file.Threshold=debug
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern= - (%r ms) - %d{yyyy-M-d HH:mm:ss}%x[%5p](%F:%L) %m%n

#DWR \u65e5\u5fd7
#log4j.logger.org.directwebremoting = ERROR

#\u663e\u793aHibernate\u5360\u4f4d\u7b26\u7ed1\u5b9a\u503c\u53ca\u8fd4\u56de\u503c
#log4j.logger.org.hibernate.type=DEBUG,CONSOLE 

#log4j.logger.org.springframework.transaction=DEBUG
#log4j.logger.org.hibernate=DEBUG
#log4j.logger.org.acegisecurity=DEBUG
#log4j.logger.org.apache.myfaces=TRACE
#log4j.logger.org.quartz=DEBUG

#log4j.logger.com.opensymphony=INFO  
#log4j.logger.org.apache.struts2=DEBUG  
log4j.logger.com.opensymphony.xwork2=debug

WeiXinController.java

package com.qhk.controller;

import java.util.Map;
import java.util.Random;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import com.qhk.util.CheckUtil;
import com.qhk.util.MessageFormat;
import com.qhk.util.MessageUtil;

@Controller
@RequestMapping("/weixin")
public class WeiXinController {
	/**
	 * <h4>功能:[微信验证 ][2018年2月9日 下午10:01:14][创建人: HongKun.Qin]</h4>
	 * <h4></h4>
	 * @param request
	 * @param response
	 * @return
	 */
	@ResponseBody
	@RequestMapping(value = "/message.do",method =RequestMethod.GET)
	public String getMessageValidate(HttpServletRequest request, HttpServletResponse response){
		String signature = request.getParameter("signature");//微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
		String timestamp = request.getParameter("timestamp");//	时间戳
		String nonce = request.getParameter("nonce");//	随机数
		String echostr = request.getParameter("echostr");//	随机字符串

		if(CheckUtil.checkSignature(signature, timestamp, nonce)){
			return echostr;
		}
		
		return "";
	}
	
	/**
	 * <h4>功能:[接受消息,并返回消息 ][2018年2月9日 下午10:02:00][创建人: HongKun.Qin]</h4>
	 * <h4></h4>
	 * @param request
	 * @param response
	 * @return
	 * @throws Exception
	 */
	@ResponseBody
	@RequestMapping(value = "/message.do",method =RequestMethod.POST)
	public String getMessage(HttpServletRequest request, HttpServletResponse response) throws Exception{
		Map<String,String> map = new MessageFormat().xmlToMap(request);
		
		String fromUserName = map.get("FromUserName");//公众号
		String toUserName = map.get("ToUserName");//粉丝号
		String msgType = map.get("MsgType");//发送的消息类型[比如 文字,图片,语音。。。]
		String content = map.get("Content");//发送的消息内容
		String message = null;
		
		System.out.println("fromUserName:"+fromUserName+"   ToUserName:"+toUserName+"  MsgType:"+msgType+"  "+content);
		
		//判断发送的类型是文本
		if(MessageUtil.MESSAGE_TEXT.equals(msgType)){
			//发送的内容为???时
			if("0".equals(content)){
				message = MessageFormat.initText(toUserName, fromUserName, MessageUtil.menuText());
			}else if("1".equals(content)) {
				Random random = new Random();
				message = MessageFormat.initText(toUserName, fromUserName, String.format("您本次的验证码为:%s%s%s%s", random.nextInt(10),random.nextInt(10),random.nextInt(10),random.nextInt(10)));//模拟验证码
			}else{
				message  = MessageFormat.initText(toUserName, fromUserName, "功能正在完善中,请按提示信息操作[回复'0'显示主菜单]。");
			}
		}else if(MessageUtil.MESSAGE_EVENT.equals(msgType)){//验证是关注/取消事件
			String eventType = map.get("Event");//获取是关注还是取消
			//关注
			if(MessageUtil.MESSAGE_SUBSCRIBE.equals(eventType)){
				message = MessageFormat.initText(toUserName, fromUserName, "欢迎关注青鸟ktv,回复[0]即可调出功能菜单");
			}
		}
		
		return message;
	
	}
	
	
}

AccessToken.java

package com.qhk.entity;

public class AccessToken {

	private String token;
	private int expiresIn;
	
	public String getToken() {
		return token;
	}
	public void setToken(String token) {
		this.token = token;
	}
	public int getExpiresIn() {
		return expiresIn;
	}
	public void setExpiresIn(int expiresIn) {
		this.expiresIn = expiresIn;
	}
}

BaseMessage.java

package com.qhk.entity;

public class BaseMessage {
	private String ToUserName;
	private String FromUserName;
	private long CreateTime;
	private String MsgType;
	public String getToUserName() {
		return ToUserName;
	}
	public void setToUserName(String toUserName) {
		ToUserName = toUserName;
	}
	public String getFromUserName() {
		return FromUserName;
	}
	public void setFromUserName(String fromUserName) {
		FromUserName = fromUserName;
	}
	public long getCreateTime() {
		return CreateTime;
	}
	public void setCreateTime(long createTime) {
		CreateTime = createTime;
	}
	public String getMsgType() {
		return MsgType;
	}
	public void setMsgType(String msgType) {
		MsgType = msgType;
	}
}

TextMessage.java

package com.qhk.entity;

public class TextMessage extends BaseMessage{

	private String Content;
	private String MsgId;
	
	public String getContent() {
		return Content;
	}
	public void setContent(String content) {
		Content = content;
	}
	public String getMsgId() {
		return MsgId;
	}
	public void setMsgId(String msgId) {
		MsgId = msgId;
	}
}

CheckUtil.java(token注意,我现在是qhk后面微信公众号平台配置也要用这个,自己改了的话就填自己改的)

package com.qhk.util;

import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

public class CheckUtil {
	public static final String token = "qhk";//这个地方也要注意
	public static boolean checkSignature(String signature,String timestamp,String nonce){
		String[] arr=new String[]{token,timestamp,nonce};
		//排序
		Arrays.sort(arr);
		//生成字符串
		StringBuffer content = new StringBuffer();
		for (int i = 0; i < arr.length; i++) {
			content.append(arr[i]);
		}
		//sha1加密
		String temp = getSha1(content.toString());
		return temp.equals(signature);
	}
	public static String getSha1(String str){
		if (null == str || 0 == str.length()){
			return null;
		}
		char[] hexDigits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 
				'a', 'b', 'c', 'd', 'e', 'f'};
		try {
			MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
			mdTemp.update(str.getBytes("UTF-8"));

			byte[] md = mdTemp.digest();
			int j = md.length;
			char[] buf = new char[j * 2];
			int k = 0;
			for (int i = 0; i < j; i++) {
				byte byte0 = md[i];
				buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
				buf[k++] = hexDigits[byte0 & 0xf];
			}
			return new String(buf);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return null;
	}

}

MessageFormat.java

package com.qhk.util;

import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.qhk.entity.TextMessage;
import com.thoughtworks.xstream.XStream;

/**
 * <p>将发送的消息进行转换</p>
 * @author HongKun.Qin
 */
public class MessageFormat {
	/**
	 * xml 转 map
	 * 
	 * @param request
	 * @return
	 * @throws IOException
	 * @throws DocumentException
	 */
	public static Map<String, String> xmlToMap(HttpServletRequest request)
			throws IOException, DocumentException {
		Map<String, String> map = new HashMap<String, String>();
		SAXReader reader = new SAXReader();
		InputStream ins = request.getInputStream();
		Document doc = reader.read(ins);
		Element root = doc.getRootElement();
		List<Element> list = root.elements();
		for (Element e : list) {
			map.put(e.getName(), e.getText());
		}
		ins.close();
		return map;
	}

	/**
	 * 将文本消息转换为xml
	 * 
	 * @param textMessage
	 * @return
	 */
	public static String textMessageToXml(TextMessage textMessage) {
		XStream xStream = new XStream();
		xStream.alias("xml", textMessage.getClass());
		return xStream.toXML(textMessage);
	}

	
	public static String initText(String toUserName, String fromUserName,
			String content) {
		TextMessage text = new TextMessage();
		text.setFromUserName(toUserName);
		text.setToUserName(fromUserName);
		text.setMsgType(MessageUtil.MESSAGE_TEXT);
		text.setCreateTime(new Date().getTime());
		text.setContent(content);
		return textMessageToXml(text);
	}
}

MessageUtil.java

package com.qhk.util;

public class MessageUtil {
	/**
	 * 类型
	 */
	public static final String MESSAGE_TEXT = "text";//文本
	public static final String MESSAGE_NEWS = "news";
	public static final String MESSAGE_IMAGE = "image";
	public static final String MESSAGE_MUSIC = "music";
	public static final String MESSAGE_VOICE = "voice";
	public static final String MESSAGE_VIDEO = "video";
	public static final String MESSAGE_LINK = "link";
	public static final String MESSAGE_LOCATION = "location";
	public static final String MESSAGE_EVENT = "event";
	public static final String MESSAGE_SUBSCRIBE = "subscribe";
	public static final String MESSAGE_UNSUBSCRIBE = "unsubscribe";
	public static final String MESSAGE_CLICK = "CLICK";
	public static final String MESSAGE_VIEW = "VIEW";
	public static final String MESSAGE_SCANCODE = "scancode_push";

	
	/**
	 * <h4>功能:[显示的主菜单 ][2018年2月9日 下午9:37:56][创建人: HongKun.Qin]</h4>
	 * <h4></h4>
	 * @return
	 */
	public static String menuText() {
		StringBuffer sb = new StringBuffer();
		sb.append("欢迎您关注青鸟KTV,请按照菜单提示进行操作:\n\n");
		sb.append("[1].显示短信验证码\n");
		sb.append("[2].显示个人信息\n");
		sb.append("[3].关于青鸟KTV\n");
		sb.append("[4].关于注册成为会员\n\n");
		sb.append("回复 \"[0]\" 调出此菜单。");
		return sb.toString();
	}
}

WeixinUtil.java(APPID、APPSECRET)别忘改成自己的,不然项目也没法运行

package com.qhk.util;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import com.qhk.entity.AccessToken;

import net.sf.json.JSONObject;


public class WeixinUtil {

	private static final String APPID="";//在基础配置中可查看自己APPID
	private static final String APPSECRET="";//在基础配置中可查看自己APPSECRET
	private static final String ACCESS_TOKEN_URL="https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
	private static final String UPLOAD_URL = "https://api.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE";
	public static JSONObject doGetStr(String url){
		DefaultHttpClient httpClient = new DefaultHttpClient();
		HttpGet httpGet=new HttpGet(url); 
		JSONObject jsonObject = null;
		try {
			HttpResponse response=httpClient.execute(httpGet);
			HttpEntity entity = response.getEntity();
			if(entity!=null){
				String result = EntityUtils.toString(entity,"UTF-8");
				jsonObject = JSONObject.fromObject(result);
			}
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		System.out.println(jsonObject);
		return jsonObject;
	}
	/**
	 * 
	 * @Description: TODO 获取AccessToken
	 * @param @return   
	 * @return AccessToken  
	 * @throws
	 * @author qinhongkun
	 * @date 2017-12-18
	 */
	public static AccessToken getAccessToken(){
		AccessToken token = new AccessToken();
		String url = ACCESS_TOKEN_URL.replace("APPID", APPID).replace("APPSECRET", APPSECRET);
		JSONObject jsonObject = doGetStr(url);
		if(jsonObject!=null){
			token.setToken(jsonObject.getString("access_token"));
			token.setExpiresIn(jsonObject.getInt("expires_in"));
		}
		return token;
	}
	
	/*
	 * 文件上传
	 */
	public static String upload(String filePath, String accessToken,String type) throws IOException, NoSuchAlgorithmException, NoSuchProviderException, KeyManagementException {
		System.out.println("filePath:"+filePath);
		File file = new File(filePath);
		if (!file.exists() || !file.isFile()) {
			throw new IOException("文件不存在");
		}

		String url = UPLOAD_URL.replace("ACCESS_TOKEN", accessToken).replace("TYPE",type);
		
		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);

		StringBuilder sb = new StringBuilder();
		sb.append("--");
		sb.append(BOUNDARY);
		sb.append("\r\n");
		sb.append("Content-Disposition: form-data;name=\"file\";filename=\"" + file.getName() + "\"\r\n");
		sb.append("Content-Type:application/octet-stream\r\n\r\n");

		byte[] head = sb.toString().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();

		StringBuffer buffer = new StringBuffer();
		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);
			}
			if (result == null) {
				result = buffer.toString();
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (reader != null) {
				reader.close();
			}
		}

		JSONObject jsonObj = JSONObject.fromObject(result);
		System.out.println(jsonObj);
		String typeName = "media_id";
		if(!"image".equals(type)){
			typeName = type + "_media_id";
		}
		String mediaId = jsonObj.getString(typeName);
		return mediaId;
	}
	
}

2.大家现在就是关心ngrok下载地址:https://ngrok.com/download

还有人会问:哎哟我靠,什么是ngrok?

其实不用纠结这是个啥,如果你作为一个Java开发人员是第一次接触微信公众号后台开发,我建议你千万别纠结这到底是个

啥,简单明了的告诉你:微信公众号开发文档中 要求 有服务器 (自己的域名、也可以理解为自己的空间 当然 不是QQ空

间),然后呢,那你就想百度一下了,怎么拥有自己的域名或者说空间,有人推荐你使用百度BEA(好像是,不知道名字有

没有记错),然后微信公众号的技术文档里面推荐你用腾讯的什么什么,总之,收费。诶,对 就是收费。

所以呢,这个ngrok就是免费的。而且运行极其简单,对,不费劲哈。

这个下载之后,暂时放那,别动。

3.注册订阅号 去微信公众号平台注册一个个人的订阅号就行,登录进去,三个箭头分别点击一下将最下面红箭头标出的地方进

行关闭
这里写图片描述
关闭后
这里写图片描述
看图操作
这里写图片描述这里写图片描述这里写图片描述
此时 我们还需要配置下图

配置你的服务器地址 (这是问题1)

配置你的token (这是问题2)

配置你的EncodingAESkey (这不是问题!!)
这里写图片描述
问题1:

然后你肯定进入了死胡同,想说URL怎么填呢?

你还记得你的ngrok吗?

首先我们先在本地启动项目,(注意代码中需要修改的地方上面我已经写出来,填写自己的APPID和密码)必须是tomcat的8080

端口

这时候就要用到ngrok了

打开你的ngrok文件夹,在包涵ngrok.exe文件的文件夹中运行cmd,然后输入指令:

ngrok -config ngrok.cfg -subdomain qinhongkun 8080

qinhongkun可以随便改,自己填写服务器地址别写错了就行。
这里写图片描述

如果没有什么问题的话下面这张图就是启动成功的(别关掉这dos窗口)
这里写图片描述
转回来微信公众号,你的URL就填写ngrok运行后的生成的网址(我已经用红色标记出来了用哪一个都行有两个)/项目名称/控

制器名 比如我的就是 https://qinhongkun.tunnel.echomod.cn/weixintest/weixin/message.do

至于为什么我的项目名是weixintest而不是weixin-demo 那是我之前在eclipse中改了项目名,而tomcat中没有修改过来,

weixin/message.do这控制器方法上面有。

问题2:

token 就要填我们在项目中写的那个,上面我也重点标记了,如果项目中更改了的话,这别忘了修改!

问题3:直接点击随机生成一个就ok了

如果没有问题现在已经能保存了。

我将项目上传到码云,https://gitee.com/it_qin/weixintest.git。

注:因为当时用作项目中有很多功能,所以这是个完整的ssm框架,只不过我把那些都删了,只留下一个简单的微信Demo.

现在去你的公众号测试一下就行了,有什么问题或者建议欢迎评论,确保一天之内回复,不算星期天哦。

猜你喜欢

转载自blog.csdn.net/Qin_HongKun/article/details/79293585