•  对于前端开发而言,微信小程序因为其简单快速、开发成本低、用户流量巨大等特点,也就成了前端开发工程师必会的一个技能。   2.先看看小程序效果 (1)欢迎页     (2)首页:轮播头图,...

    一.写在前面

    1.为什么要学小程序开发?

        对于前端开发而言,微信小程序因为其简单快速、开发成本低、用户流量巨大等特点,也就成了前端开发工程师必会的一个技能。

     

    2.先看看小程序效果

    (1)欢迎页

     

     

    (2)首页:轮播头图,天气,豆瓣电影正在热映

     

     

    (3)全国城市切换页

     

    (4)天气详情页

     

    (5)地图周边服务

     

    (6)豆瓣电影

     

     

    (7)热点新闻

     

    (8)更多页面

     

    3.开发准备:

    1)有人开玩笑说,会vue小程序根本都不用学:

    微信小程序虽然是腾讯自己搞的,但是核心的思想跟vue等框架是一样一样的哦~

    2)善于搜集精美的小组件: “我们不生产代码,我们只是代码的搬运工”,善于找到想要的组件并把他们巧妙优雅的组装成一个大项目,也算是程序员一项基本技能了。

    具体怎么找到想要的小程序demo,篇末会给大家推荐小程序的资源,有很多大神的项目哦

     

     

     

     撸起袖子开干了

     

    一.注册小程序账号,下载IDE

    1.官网注册https://mp.weixin.qq.com/,并下载IDE。

    2.官方文档一向都是最好的学习资料。

    注意:

    (1)注册账号之后会有一个appid,新建项目的时候需要填上,不然很多功能是用不了的,比如不能预览,不能上传代码等等。

    (2)如果你注册过微信公众号的话,一定要注意,微信公众号和小程序是两个账号,二者的appid也是不同,小程序开发必须使用小程序的appid哦。

     

    二.小程序框架介绍和运行机制

    1.我们建立了“普通快速启动模板”,然后整个项目目录如下:

    2.app.js

    整个项目的启动文件,如注释写的onlaunch方法有三大功能,浏览器缓存进行存和取数据;用登陆成功的回调;获取用户信息。

    globalData是定义整个项目的全局变量或者常量哦。

     

    3.app.json

    整个项目的配置文件,比如注册页面,配置tab页,设置整个项目的样式,页面标题等等;

    !注意:小程序启动默认的第一个页面,就是app.json的pages中的第一个页面哦。

    4.pages

    小程序的页面组件,有几个页面就会有几个子文件夹。比如快速启动模板,就有两个页面,index和logs

    5.打开index目录

    可以看到有三个文件,其实和我们web开发的文件是一一对应的。

    index.wxml对应index.html;

    index.wxss对应index.css;

    index.js就是js文件哦。

    一般我们还会给每个页面组件添加一个.json文件,作为该页面组件的配置文件,设置页面标题等功能

    6.双击index.js文件

    (1)var app = getApp(); 

    引入整个项目的app.js文件,用来取期中的公共变量等信息。

    如果要使用util.js工具库中的某个方法,在util.js中module.exports导出,然后在需要的页面中require即可得到哦。

    (2)比如,我们要获取豆瓣电影的时候,我们需要调用豆瓣的api;我们先在app.js中的gloabData中定义doubanBase

    然后在index.js中使用app.globaData.doubanBase即可取到这个值。

    当然这些常量你也可以在页面需要的时候,再用写死的值,但是为了整个项目的维护,还是建议把这种公用参数统一写在配置文件中哦。

     

    (3)接下来在整个page({})中,第一个data,就是本页面组件的内部数据,会渲染到该页面的wxml文件中,类似于vue、react哦~

    通过setData修改data数据,驱动页面渲染

    (4)一些生命周期函数

    比如onload(), onready(), onshow(), onhide()等等,监听页面加载、页面初次渲染、页面显示、页面隐藏等等

    更多的可以查官网API哦。其中用的最多的就是onload()方法,和onShareAppMessage()方法(设置页面分享的信息)

     

    7 .wxml模板的使用。

    比如本项目电影页面,就是以最小的星级评价组件wxml当做模板,star到movie到movie-list,一级一级的嵌套使用。

     star-template.wxml页面写好name属性;然后import引入的时候通过name获得即可

     

    8.常用的wxml标签

    view,text,icon,swiper,block,scroll-view等等,这些标签直接查官网文档即可

     

     

    三.小程序框架、各个页面以及发布上线的注意点

     

    1.整个框架中的一些注意点

    (1)整个wxml页面,最底层的标签是<page></page>哦。

    (2) 每个页面顶部导航栏的颜色,title在本页面的json中配置,如果没有配置的话,取app.json中的总配置哦。

    (3)json中不能写注释哦,不然会报错的。

    (4)路由相关

    1)使用wx.SwitchTab跳转tab页的话,在app.json中除了注册pages页面,还需要在tabBar中注册tab页,才能生效哦。

    注意:tab最多5个,也就是我们说的头部或者底部最多5个菜单。其他的页面只能通过其他路由方法打开哦。

    2)navigateTo是跳到某个非tab页,比如欢迎页,电影详情页,城市选择页;在app.json中注册后,不能在tabBar里注册哦,不然同样也是不能跳转的哦。

    3)reLaunch跳转,新开的页面左上角是没有退回按钮的,本项目只用了一次,切换城市的时候哦。

    (5)页面之间传递参数

    参数写在跳转的url之中,然后另一个页面在onload方法中的传参option接收到。如下传递和获取id

     

    (6)data-开头的自定义属性的使用

    比如wxml中我们怎么写 

    点击的事件对象可以这么取,var postId = event.currentTarget.dataset.postid;

    注意: 大写会转换成小写,带_符号会转成驼峰形式

    (7)事件对象event,event.target和event.currentTarget的区别:

    target指的是当前点击的组件 和currentTarget 指的是事件捕获的组件。

    比如,轮播图组件,点击事件应该要绑定到swiper上,这样才能监控任意一张图片是否被点击,

    这时target这里指的是image(因为点击的是图片),而currentTarget指的是swiper(因为绑定点击事件在swiper上)

    (8)使用免费的网络接口:

    本项目中用到了 和风天气api,腾讯地图api,百度地图api,豆瓣电影api,聚合头条新闻api等,具体用法可以看各自官网的接口文档哦,很详细的

    注意:免费接口是有访问限制的,所以如果用别人的组件用了这种接口的话,最好还是自己注册一个新的key替换上哦

    附上一个免费接口大全:

    https://github.com/jokermonn/-Api

    !!另外还要注意,要把这些接口的域名配置到小程序的合法域名中,不然也是访问不了的

    (8)wxss有一个坑:无法读取本地资源,比如背景图片用本地就会报错哦。

    把本地图片弄成网络图片的几种方式: 上传到个人网站;QQ空间相册等等也是可以的哦

     

     

    2.切换城市页面:

    (1)首页使用navigateTo跳转到切换城市页,由于首页并没有关闭,导致切换了城市返回来,天气信息还是旧的。

    正确的处理逻辑如下:

    1)使用reLaunch跳转到切换城市页面,实质是关闭所有页面打开新的页面哦。

    2)切换城市页面,更新公共变量中城市信息为手动切换的城区,再switchTab回到首页,触发首页重新加载。

    3)首页获取城市信息的时候加一个判断,全局没有才取定位的,全局有(比如刚才设置了)就用全局的哦。

    (2)城市列表的滚动和回到顶部

    基于scroll-view组件的scroll-top属性,初始就是0,滚动就会增加的;点击回到顶部给它置为0即可回到顶部

     

     

    3.天气页

    (1)初始化页面,天气显示的逻辑

    首先调用小程序的wx.getLocation方法获得当前的经纬度,然后调用腾讯地图获得当前的城市名称和区县名称,并存到公共变量中,

    再调用查询天气和空气质量的方法哦。

    (2)容错处理

    城市的名称长短不一,有点名字特别长,比如巴彦淖尔市这种,需要动态的获取完整的城市名称;

    有些偏僻的城市暂时没有天气信息,我们需要对返回的结果进行判断,没有信息的需要给用户一个良好的提示信息。

     

     

    4.周边-地图服务页面

    (1)调用百度地图的各种服务,查询酒店,美食,生活服务三种信息,更多信息可以看百度地图的文档

    (2)点击时给被点中的图标加个边框,数据驱动视图,所以使用一个长度为3的数组保存三个图标当前是否被点中的状态

    然后wxml再根据数据来动态添加class,增加边框样式

     

    5.豆瓣电影页

    (1)电影详情页的预览图片,用小程序本身的previewImage实现。

    (2)详情页使用onReachBottom()方法,监控用户上拉触底事件,然后发送请求继续获得数据,实现懒加载的效果

    (3)用户体验方面的优化,js中将整数评分比如7分统一改为7.0分,然后wxml模板再判断分数是否为0显示“暂无评分”

    (4)搜索之后清空搜索框

    因为小程序中不能使用getelementbyId这种方式获得元素,只能用数据来控制了

    在data中加一个属性searchText来保存搜索框的内容并和 input的value属性绑定,搜索完成或者点击X时,searchText变量清空即可实现清空输入框的效果哦。

     

    6.新闻页面

    (1)聚合头条新闻的免费接口,只返回了新闻的基本信息,新闻的主体内容是没有的哦。

    我找了好多新闻类的接口,好像都是没有新闻主体内容的。如果谁知道更好的接口欢迎留言告诉我哈~

    (2)当然,也可以自己去爬新闻网站的数据哦

     

    7.更多页面

     (1)小程序目前开放外链的功能只是给公司组织的小程序开放了,个人开发还是不能使用外链的哦。

     (2)彩蛋页面,获得用户信息

    通过 wx.setStorageSync('userInfos', userInfos);  可以获得登陆小程序的用户的个人信息,可以发送给后台存到数据库中,方便对用户进行分析

    我这里只是存储到浏览器缓存中哦,最大应该是10M缓存;如果用户把这个小程序从小程序列表中删除掉,就会清空这个缓存。

     

    8.发布注意

    (1) 新版本小程序发布的限制为2M,一般都是图片最占空间,所以尽量使用网络图片

    具体怎么把本地图片变成网络图片,上面有讲哦。

    (2)在开发者工具上预览测试没问题,点击上传;网页版小程序个的人中心的左侧“开发管理”菜单,第三块--开发版本就有了内容。

    (3)点击提交,填写小程序相关信息,就可以提交审核了哦。

    注意:分类最好填写准确,这样才能更快的通过审核哦。我这个小程序一天半时间过审上线的

     

     

    至此,我就把两天开发内碰到的坑和注意点都过了一遍,据说还有更多的坑,等之后更深入的开发再继续研究咯。

     

     

    四.写在最后

    1.推荐几个小程序开发的资料

    (1)知乎一篇小程序的资料:

    https://www.zhihu.com/question/50907897

    (2)小程序社区

    http://www.wxapp-union.com/portal.php

    (3)极乐小程序商店

    http://store.dreawer.com/

    2.本项目的github地址

     https://github.com/yllg/WXxcx

      


    转自:https://www.cnblogs.com/xuanbiyijue/p/7980010.html
    展开全文
  • 但是,今天我所要讲的并不是对于小程序的开发,而是要说一下,关于微信开发的另外一个内容,那就是微信公众号。。  关于,什么是微信公众号,微信公众号怎么申请,这个我就不多说,这些基本的概念不在这里进行讲解...

         最近的话,发现微信开发其实也有很多挺有意思的地方,比如最近很火的一款游戏“跳一跳”,也让我如此着迷。。但是,今天我所要讲的并不是对于小程序的开发,而是要说一下,关于微信开发的另外一个内容,那就是微信公众号。。

        关于,什么是微信公众号,微信公众号怎么申请,这个我就不多说,这些基本的概念不在这里进行讲解,自己可以直接百度就可以找到很多的资源。而我主要讲解一下关于微信公众号开发中,一些比较重要和常见的知识点,所以,这个也并不是基础篇的文章哦~!好歹也要对微信公众号有一点了解才行。~!

    一:实现外网域名访问本地服务项目

    描述:我们在刚开始接触微信公众号开发的时候,我想,一般情况下,很多人都是没有服务器的,简单点说,就是没有服务器IP,或者是公网访问的域名。然而,要想能够实现微信公众能能够与我们后台代码进行交互,那么肯定就需要有一个公网能够访问的地址,那么这怎么办好呢?

    解法一:很简单呀,直接去新浪云,阿里云,华为云,买一个服务器呗,而且是学生的话,还有优惠,多好。。但是,这个又比较繁琐,里面又有配置内容啥的,一堆(比如,tomcat,mysql,jdk等等)。那么,看看第二种方法。。。。(但是,请记住,这个是想做服务端开发必须会的,如果你连部署项目都不会,你觉得公司对你的感觉如何?多一个技能就是一个优势)

    解法二:反向代理。。如果,你是第一次听说这个名词,那么就赶紧恶补一下,这个名词的含义。我简单点说,就是通过反向代理的模式,来代理你本地的ip,以便能够在公网地址能够访问到本地的项目。。具体,请看下面,如何进行~!~!

    步骤:(1)下载ogrok客户端---------也就是反向代理的客户端,当然还有很多类似的,我这里就使用这个而已。

    这个可以通过该链接进行下载ogrok下载链接

    (2)解压刚下载好的客户端文件到自己的一个目录下

    (3)通过cmd命令,进入DOS,并且进入到刚刚解压的ogrok目录下

    (4)执行 ngrok -config=ngrok.cfg -subdomain xxx 8080 //(xxx 是你自定义的域名前缀)

    比如,我这里就是xxx就是用myjavttest

    结果:

    (5)这样的话,我们就可以通过上面的地址进行访问本地的项目了。(原来都是用的localhost:8080/login.jsp或者127.0.0.1:8080/login.jsp),当然,前提是本地有一个开启的项目,这样才可以,别本地都没有项目开启,就用公网去访问,你觉得,它能够访问么?所以,请别忘记这一点。

    (二)微信公众号客户端与后台进行验证身份

    首先,我们通过第一步,我们就能够得到一个以公网地址访问的一个IP(域名),那么既然要使得微信公众号能够与后台进行关联,那么肯定需要配置微信公众号的具体对应的服务器地址了。

    步骤:(1)首先,进入微信公众号开发官网,并且进行登陆

    (2)

    (3)

    (4)

    OK,配置到这个的话,就简单的,将基本的配置进行设置好了。。那么,下面才是关键,进入真正的开发阶段。。

    如果微信端,要与后台进行关联,那么当用户进行与后台交互的时候,后台就需要采取,身份验证,而这个是通过GET方式的请求,而只有通过的才可以进行后续的处理。所以,如何进行,那么就看下面的代码:这里讲解两种形式~~~~

    第一种:(采取Servlet)

    @WebServlet(name = "ConnectWeChatServlet")
    public class ConnectWeChatServlet extends HttpServlet {
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        }
    
        /**
         * 进行验证是否身份匹配
         * @param request
         * @param response
         * @throws ServletException
         * @throws IOException
         */
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            String signature = request.getParameter("signature");
            String timestamp = request.getParameter("timestamp");
            String nonce = request.getParameter("nonce");
            String echostr = request.getParameter("echostr");
            System.out.println(""+signature +"@"+timestamp +"$"+nonce +"^"+echostr);
            PrintWriter out = response.getWriter();
            if(CheckConnectUtils.checkConncetWithWeChat(signature,timestamp,nonce)){
                out.print(echostr);
            }
        }

    验证的代码:

    package com.hnu.scw.utils;
    import java.security.MessageDigest;
    import java.security.NoSuchAlgorithmException;
    import java.util.Arrays;
    /**
     * @author scw
     * @create 2018-01-17 9:28
     * @desc 检查微信和服务器是否链接成功
     **/
    public class CheckConnectUtils {
        private static final String token = "wechat"; //这个就是微信公众号之前配置的token,必须保持一致
        /**
         * 判断是否链接匹配
         * @param signature
         * @param timestamp
         * @param nonce
         * @return
         */
        public static boolean checkConncetWithWeChat(String signature,String timestamp,String nonce){
            String[] arr = new String[]{token,timestamp,nonce};
            //排序
            Arrays.sort(arr);
            //生成字符串
            StringBuilder stringBuilder = new StringBuilder();
            for (String str:arr) {
                stringBuilder.append(str);
            }
            //进行SHA1加密
            String encodeString = passSha1Encode(stringBuilder.toString());
            if(signature.equals(encodeString)){
                return true;
            }else{
                return false;
            }
        }
    
        /**
         * 字符串进行SHA1加密
         * @param str
         * @return
         */
        public static String passSha1Encode(String str){
            if(str == null || str.length() == 0){
                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());
                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) {
               return null;
            }
        }
    }

    第二种:(采取框架,比如SpringMVC+Spring+Hibernate)

    /**
     * @author scw
     * @create 2018-01-18 11:38
     * @desc 微信前端连接的主要控制类
     **/
    @Controller
    public class WeChatDogPrimaryController {
        /**
         * 进行微信用户验证,只能是Get方法
         * @param request
         * @param response
         */
        @RequestMapping(value = "/wechat" ,method = RequestMethod.GET)
        public void connectValidate(HttpServletRequest request , HttpServletResponse response) throws IOException {
            String signature = request.getParameter("signature");
            String timestamp = request.getParameter("timestamp");
            String nonce = request.getParameter("nonce");
            String echostr = request.getParameter("echostr");
            System.out.println(""+signature +"@"+timestamp +"$"+nonce +"^"+echostr);
            PrintWriter out = response.getWriter();
            if(CheckConnectUtils.checkConncetWithWeChat(signature,timestamp,nonce)){
                out.print(echostr);
            }
        }
    
    
        /**
         * 客户端进行的消息处理
         * @param request
         * @param response
         */
        @RequestMapping(value = "/wechat" ,method = RequestMethod.POST)
        public void disposeClientMessage(HttpServletRequest request , HttpServletResponse response ) throws IOException {
          
        }

    (三)微信客户端与后台进行消息交互(比如,文本,图片,视频,音频)

    消息的实体类:

    package com.hnu.scw.model;
    
    /**
     * @author scw
     * @create 2018-01-17 13:37
     * @desc 对于所有消息的基本父类
     **/
    public class BaseMessage {
        private String ToUserName;
        private String FromUserName;
        private String 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 String getCreateTime() {
            return CreateTime;
        }
    
        public void setCreateTime(String createTime) {
            CreateTime = createTime;
        }
    
        public String getMsgType() {
            return MsgType;
        }
    
        public void setMsgType(String msgType) {
            MsgType = msgType;
        }
    }
    
    package com.hnu.scw.model;
    
    /**
     * @author SCW
     * @create 2018-01-17 15:08
     * @desc 图片的基本类
     **/
    public class ImageBase {
        private String MediaId;
    
        public String getMediaId() {
            return MediaId;
        }
    
        public void setMediaId(String mediaId) {
            MediaId = mediaId;
        }
    }
    
    package com.hnu.scw.model;
    
    /**
     * @author scw
     * @create 2018-01-17 15:09
     * @desc 图片消息
     **/
    public class ImageMessage extends BaseMessage {
        private ImageBase Image ;
    
        public ImageBase getImageBase() {
            return Image;
        }
    
        public void setImageBase(ImageBase Image) {
            this.Image = Image;
        }
    }
    
    package com.hnu.scw.model;
    
    /**
     * @author Administrator
     * @create 2018-01-17 16:45
     * @desc 音乐类型的基本类
     **/
    public class MusicBase {
        private String Title;
        private String Description;
        private String MusicUrl;
        private String HQMusicUrl;
        private String ThumbMediaId;
    
        public String getTitle() {
            return Title;
        }
    
        public void setTitle(String title) {
            Title = title;
        }
    
        public String getDescription() {
            return Description;
        }
    
        public void setDescription(String description) {
            Description = description;
        }
    
        public String getMusicUrl() {
            return MusicUrl;
        }
    
        public void setMusicUrl(String musicUrl) {
            MusicUrl = musicUrl;
        }
    
        public String getHQMusicUrl() {
            return HQMusicUrl;
        }
    
        public void setHQMusicUrl(String HQMusicUrl) {
            this.HQMusicUrl = HQMusicUrl;
        }
    
        public String getThumbMediaId() {
            return ThumbMediaId;
        }
    
        public void setThumbMediaId(String thumbMediaId) {
            ThumbMediaId = thumbMediaId;
        }
    }
    
    package com.hnu.scw.model;
    
    /**
     * @author Administrator
     * @create 2018-01-17 16:46
     * @desc 用于包装音乐的实体
     **/
    public class MusicMessage extends BaseMessage {
        private MusicBase Music;
    
        public MusicBase getMusic() {
            return Music;
        }
    
        public void setMusic(MusicBase music) {
            Music = music;
        }
    }
    
    package com.hnu.scw.model;
    
    /**
     * @author scw
     * @create 2018-01-17 10:03
     * @desc 文本消息的内容
     **/
    public class MyTestMessage  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;
        }
    }
    
    package com.hnu.scw.model;
    
    /**
     * @author scw
     * @create 2018-01-17 13:38
     * @desc 对于图文消息最内层结构的实体类
     **/
    public class NewsBase {
        private String Title;
        private String Description;
        private String PicUrl;
        private String Url;
    
        public String getTitle() {
            return Title;
        }
    
        public void setTitle(String title) {
            Title = title;
        }
    
        public String getDescription() {
            return Description;
        }
    
        public void setDescription(String description) {
            Description = description;
        }
    
        public String getPicUrl() {
            return PicUrl;
        }
    
        public void setPicUrl(String picUrl) {
            PicUrl = picUrl;
        }
    
        public String getUrl() {
            return Url;
        }
    
        public void setUrl(String url) {
            Url = url;
        }
    }
    
    package com.hnu.scw.model;
    
    import java.util.List;
    
    /**
     * @author scw
     * @create 2018-01-17 13:35
     * @desc 对于图文消息的实体类
     **/
    public class NewsMessage extends BaseMessage{
        private int ArticleCount;
        private List<NewsBase> Articles;
    
        public int getArticleCount() {
            return ArticleCount;
        }
    
        public void setArticleCount(int articleCount) {
            ArticleCount = articleCount;
        }
    
        public List<NewsBase> getArticles() {
            return Articles;
        }
    
        public void setArticles(List<NewsBase> articles) {
            Articles = articles;
        }
    }

    消息封装工具类:

    package com.hnu.scw.utils;
    import com.hnu.scw.model.*;
    import com.thoughtworks.xstream.XStream;
    import org.dom4j.Document;
    import org.dom4j.Element;
    import org.dom4j.io.SAXReader;
    import javax.servlet.http.HttpServletRequest;
    import java.io.InputStream;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    /**
     * @author scw
     * @create 2018-01-17 9:52
     * @desc 用户转换消息的格式
     **/
    public class MessageUtils {
        /**
         * 定义多种消息类型
         */
        public static final String MESSAGE_TEXT = "text";
        public static final String MESSAGE_IMAGE = "image";
        public static final String MESSAGE_VOICE = "voice";
        public static final String MESSAGE_MUSIC = "music";
        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";
    
        /**
         * XML格式转为map格式
         * @param request
         * @return
         */
        public static Map<String , String> xmlToMap(HttpServletRequest request){
            Map<String ,String> map = new HashMap<String , String>();
            try {
                InputStream inputStream =null;
                inputStream = request.getInputStream();
                SAXReader reader = new SAXReader();
                Document doc = reader.read(inputStream);
                Element rootElement = doc.getRootElement();
                List<Element> elements = rootElement.elements();
                for (Element el:elements) {
                    map.put(el.getName() , el.getText());
                }
                inputStream.close();
                return map ;
            } catch (Exception e) {
                e.printStackTrace();
                return null ;
            }
        }
        /**
         * 文本消息对象转为xml格式
         * @param myTestMessage
         * @return
         */
        public static String textMessage2Xml(MyTestMessage myTestMessage){
            XStream xStream = new XStream();
            xStream.alias("xml" , myTestMessage.getClass());
            return xStream.toXML(myTestMessage);
        }
        /**
         * 将图文消息对象转化为图文格式的XML
         * @return
         */
        public static String newsMessage2XML(NewsMessage newsMessage){
            XStream xStream = new XStream();
            //将需要修改的一些标签进行替换
            xStream.alias("xml" , newsMessage.getClass());
            xStream.alias("item" , new NewsBase().getClass());
            return xStream.toXML(newsMessage);
        }
        /**
         * 设置需要返回的图文信息
         * @param fromUserName
         * @param toUserName
         * @return
         */
        public static String initNewSMessage(String fromUserName , String toUserName ){
            String message = null;
            List<NewsBase> newList = new ArrayList<NewsBase>();
            NewsMessage newsMessage = new NewsMessage();
            NewsBase  newsBase = new NewsBase();
            newsBase.setTitle("我是图文消息");
            newsBase.setDescription("测试测试测试测试测试测试测试测试测试");
        newsBase.setPicUrl("https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=677717294,4155848424&fm=27&gp=0.jpg")
            newsBase.setUrl("www.baidu.com");
            //添加图文消息的内容
            newList.add(newsBase);
    
            //注意接受消息和发送消息的顺序要反过来,因为现在是从服务器进行发送了,而客户端是接收端了
            newsMessage.setFromUserName(toUserName);
            newsMessage.setToUserName(fromUserName);
            newsMessage.setCreateTime(String.valueOf(System.currentTimeMillis()));
            newsMessage.setMsgType("news");
            newsMessage.setArticleCount(newList.size());
            newsMessage.setArticles(newList);
            //调用转为图文的XML
            return MessageUtils.newsMessage2XML(newsMessage);
        }
        /**
         * 设置需要返回的文本信息
         * @param fromUserName
         * @param toUserName
         * @param content
         * @return
         */
        public static String initText(String fromUserName , String toUserName , String content){
            MyTestMessage text = new MyTestMessage();
            //注意接受消息和发送消息的顺序要烦过来
            text.setFromUserName(toUserName);
            text.setToUserName(fromUserName);
            text.setMsgType(MessageUtils.MESSAGE_TEXT);
            long time = System.currentTimeMillis();
            text.setCreateTime(String.valueOf(time));
            text.setContent(content);
            return MessageUtils.textMessage2Xml(text);
        }
        /**
         * 设置订阅时,返回菜单的内容
         * @return
         */
        public static String menuText(){
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("菜单1:回复数字1,有惊喜\n");
            stringBuilder.append("菜单2:回复数字2,有惊喜\n");
            stringBuilder.append("菜单3:回复数字3,有惊喜\n");
            stringBuilder.append("菜单4:回复数字3,有惊喜\n");
            stringBuilder.append("菜单5:回复数字3,有惊喜\n");
            stringBuilder.append("菜单6:回复数字未知的东东,也还有有惊喜哦\n");
            return stringBuilder.toString();
        }
        /**
         * 回复关键字"1"的时候的内容
         * @return
         */
        public static String inputNumber1(){
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("我是惊喜111,哈哈,惊喜不惊喜!");
            return stringBuilder.toString();
        }
        /**
         * 回复关键字"2"的时候的内容
         * @return
         */
        public static String inputNumber2(){
            StringBuilder stringBuilder = new StringBuilder();
            stringBuilder.append("我是惊喜2222,哈哈,惊喜不惊喜!");
            return stringBuilder.toString();
        }
        /**
         * 返回图片消息(对于视频和音频都是一样的方式,只需要更改类型即可,即将Image修改为video,voice)
         * @param fromUserName
         * @param toUserName
         * @return
         */
        public static String initImageMessage(String fromUserName , String toUserName ){
            ImageBase imageBase = new ImageBase();
            //这个media_Id是在之前执行过上传图片返回得到的信息
            imageBase.setMediaId("HK17wQmCupESK4B9u14PqI4w3gtteXhUtGgriJW6G5c8O-Y0OsjGbYfQYhGDbYDx");
            ImageMessage imageMessage = new ImageMessage();
            imageMessage.setFromUserName(toUserName);
            imageMessage.setToUserName(fromUserName);
            imageMessage.setMsgType(MessageUtils.MESSAGE_IMAGE);
            long time = System.currentTimeMillis();
            imageMessage.setCreateTime(String.valueOf(time));
            imageMessage.setImageBase(imageBase);
            return imageMessage2XML(imageMessage);
        }
        /**
         * 将图片消息对象转化为图片格式的XML
         * @return
         */
        public static String imageMessage2XML(ImageMessage imageMessage){
            XStream xStream = new XStream();
            //将需要修改的一些标签进行替换
            xStream.alias("xml" , imageMessage.getClass());
            xStream.alias("Image" , new ImageBase().getClass());
            return xStream.toXML(imageMessage);
        }
        /**
         * 将音乐消息对象转化为图片格式的XML
         * @return
         */
        public static String musicMessage2XML(MusicMessage musicMessage){
            XStream xStream = new XStream();
            //将需要修改的一些标签进行替换
            xStream.alias("xml" , musicMessage.getClass());
            xStream.alias("Music" , new MusicBase().getClass());
            return xStream.toXML(musicMessage);
        }
        /**
         * 返回音乐消息
         * @param fromUserName
         * @param toUserName
         * @return
         */
        public static String initMusicMessage(String fromUserName , String toUserName ){
            MusicBase musicBase = new MusicBase();
            //这个ThumbMediaId是在之前执行过上传缩略图返回得到的信息(这个和上传图片的方法是一样的,都是调用pictureUtils中的上传方法)
            musicBase.setThumbMediaId("vJOi5E4_U91onQnsayPdkzxted6ZctEAEzcoLd3BJ8a00gJLuhEmTckF6S2XkS5R");
            musicBase.setTitle("来一首音乐");
            musicBase.setDescription("静静的听首歌吧!");
            //设置高质量音质的歌曲路径,可以和一般音质音乐的路径一样
            musicBase.setHQMusicUrl("http://myjava.ngrok.xiaomiqiu.cn/resource/mymusic.mp3");
            //设置音乐的路径
            musicBase.setMusicUrl("http://myjava.ngrok.xiaomiqiu.cn/resource/mymusic.mp3");
            MusicMessage musicMessage = new MusicMessage();
            musicMessage.setFromUserName(toUserName);
            musicMessage.setToUserName(fromUserName);
            //设置类型为音乐
            musicMessage.setMsgType(MessageUtils.MESSAGE_MUSIC);
            long time = System.currentTimeMillis();
            musicMessage.setCreateTime(String.valueOf(time));
            musicMessage.setMusic(musicBase);
            return musicMessage2XML(musicMessage);
        }
    }

    交互主类:

    package com.hnu.scw.controller;
    import com.hnu.scw.utils.CheckConnectUtils;
    import com.hnu.scw.utils.MessageUtils;
    import com.hnu.scw.utils.WeiXinUserInfoUtiols;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.util.Map;
    /**
     * @author scw
     * @create 2018-01-18 11:38
     * @desc 微信前端连接的主要控制类
     **/
    @Controller
    public class WeChatDogPrimaryController {
        /**
         * 进行微信用户验证,只能是Get方法
         * @param request
         * @param response
         */
        @RequestMapping(value = "/wechat" ,method = RequestMethod.GET)
        public void connectValidate(HttpServletRequest request , HttpServletResponse response) throws IOException {
            String signature = request.getParameter("signature");
            String timestamp = request.getParameter("timestamp");
            String nonce = request.getParameter("nonce");
            String echostr = request.getParameter("echostr");
            System.out.println(""+signature +"@"+timestamp +"$"+nonce +"^"+echostr);
            PrintWriter out = response.getWriter();
            if(CheckConnectUtils.checkConncetWithWeChat(signature,timestamp,nonce)){
                out.print(echostr);
            }
        }
        /**
         * 客户端进行的消息处理
         * @param request
         * @param response
         */
        @RequestMapping(value = "/wechat" ,method = RequestMethod.POST)
        public void disposeClientMessage(HttpServletRequest request , HttpServletResponse response ) throws IOException {
            backTestFunction(request , response );
        }
        /**
         * 文字回复功能开发
         * @param request
         * @param response
         * @throws ServletException
         * @throws IOException
         */
        public void backTestFunction(HttpServletRequest request , HttpServletResponse response ) throws IOException {
            //防止进行post提交和响应的消息乱码
            request.setCharacterEncoding("UTF-8");
            response.setHeader("Content-type", "text/html;charset=UTF-8");
            response.setCharacterEncoding("UTF-8");
            PrintWriter out = response.getWriter();
            try{
                //将发送过来的消息XML形式转为map内容
                Map<String , String> map = MessageUtils.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 ;
                if(MessageUtils.MESSAGE_TEXT.equals(msgType)){
                    //进行关键字回复功能
                    if("1".equals(content)){
                        message = MessageUtils.initText(fromUserName,toUserName,MessageUtils.inputNumber1());
                    }else if("2".equals(content)){
                        message = MessageUtils.initText(fromUserName,toUserName,MessageUtils.inputNumber2());
                    }
                    else if("3".equals(content)){
                        //客户端输入“3”,返回一条图文消息
                        message = MessageUtils.initNewSMessage(fromUserName,toUserName);
                    }else if("4".equals(content)){
                        //客户端输入“4”,返回一条图片消息
                        message = MessageUtils.initImageMessage(fromUserName,toUserName);
                    }else if("5".equals(content)){
                        //客户端输入“5”,返回一首音乐消息
                        message = MessageUtils.initMusicMessage(fromUserName,toUserName);
                    }else if("6".equals(content)){
                        //测试是否能够获取用户的信息
                        message = MessageUtils.initText(fromUserName,toUserName, WeiXinUserInfoUtiols.getUserInfo(fromUserName));
                    }else if(content.startsWith("翻译")){
                        //客户端输入“以翻译开头”,返回对应的翻译信息
                        /*String translateResult = TranslationUtils.translate(content.substring(2,content.length()));
                        message = MessageUtils.initText(fromUserName,toUserName , translateResult);*/
                    }else {
                        message = MessageUtils.initText(fromUserName,toUserName,"你发送的消息是:" + content);
                    }
                }else if(MessageUtils.MESSAGE_EVENT.equals(msgType)){
                    String eventType = map.get("Event");
                    //完成订阅时候返回的内容
                    if(MessageUtils.MESSAGE_SUBSCRIBE .equals(eventType)){
                        message = MessageUtils.initText(fromUserName,toUserName , MessageUtils.menuText());
                    }else if(MessageUtils.MESSAGE_CLICK .equals(eventType)){
                        //进行的是click按钮的点击事件(这里就推送一下主菜单内容)
                        message = MessageUtils.initText(fromUserName,toUserName , MessageUtils.menuText());
                    }else if(MessageUtils.MESSAGE_VIEW .equals(eventType)){
                        //进行的是view按钮的点击事件(这里就推送一下主菜单内容)
                        String viewUrl = map.get("EventKey");
                        message = MessageUtils.initText(fromUserName,toUserName , viewUrl);
                    }else if(MessageUtils.MESSAGE_SCANCODE .equals(eventType)) {
                        //进行的是扫码事件
                        String key = map.get("EventKey");
                        message = MessageUtils.initText(fromUserName,toUserName , key);
                    }
                }else if(MessageUtils.MESSAGE_LOCATION .equals(msgType)) {
                    //进行的是地理位置信息
                    String label = map.get("Label");
                    message = MessageUtils.initText(fromUserName,toUserName , label);
                }
                //打印输出的xml格式内容,方便进行调试
                System.out.println(message);
                out.print(message);
            }catch (Exception e){
                e.printStackTrace();
            }finally {
                out.close();
            }
        }
    }

    (四)获取Access_Token

    Access_Token是一个全局的票据,调用任何的高级接口,都需要这个,所以这个非常非常的重要

    具体代码:

    package com.hnu.scw.utils;
    import com.hnu.scw.menu.BaseButton;
    import com.hnu.scw.menu.ClickButton;
    import com.hnu.scw.menu.CustomeMenu;
    import com.hnu.scw.menu.ViewButton;
    import com.hnu.scw.model.AccessToken;
    import com.hnu.scw.projectconst.ProjectConst;
    import net.sf.json.JSONObject;
    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.client.methods.HttpPost;
    import org.apache.http.entity.StringEntity;
    import org.apache.http.impl.client.DefaultHttpClient;
    import org.apache.http.util.EntityUtils;
    import org.springframework.web.context.ContextLoader;
    import org.springframework.web.context.WebApplicationContext;
    
    import javax.servlet.ServletContext;
    import java.io.IOException;
    import java.io.UnsupportedEncodingException;
    
    /**
     * @author scw
     * @create 2018-01-17 14:13
     * @desc 用户获取access_token,众号调用各接口时都需使用access_token
     **/
    public class WeiXinUtils {
        /**
         * 微信公众号的APPID和Appsecret,这个是每个微信公众号都唯一的,以后配置不同的公众号配置这里即可
         */
        private static final String APPID = "自己公众号对应的内容";
        private static final String APPSECRET = "自己公众号对应的内容";
        //获取access_token的URL
        private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
    
        //进行创建菜单的接口URL
        private static final String CREATE_MENU_URL ="https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
    
        //菜单查询的接口URL
        private static final String QUERY_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN";
    
        //菜单删除的接口URL
        private static final String DELETE_MENU_URL = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN";
    
        /**
         * Get请求,方便到一个url接口来获取结果
         * @param url
         * @return
         */
        public static JSONObject doGetStr(String url){
            DefaultHttpClient defaultHttpClient = new DefaultHttpClient();
            HttpGet httpGet = new HttpGet(url);
            JSONObject jsonObject = null;
            try{
                HttpResponse response = defaultHttpClient.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();
            }
            return jsonObject;
        }
    
        /**
         * 带参数的post请求,方便到一个url接口来获取结果
         * @param url
         * @param outStr
         * @return
         */
        public static JSONObject doPostStr(String url , String outStr)  {
            DefaultHttpClient defaultHttpClient = new DefaultHttpClient();
            HttpPost httpPost = new HttpPost(url);
            JSONObject jsonObject = null;
            try {
                httpPost.setEntity(new StringEntity(outStr , "UTF-8"));
                HttpResponse response = defaultHttpClient.execute(httpPost);
                HttpEntity entity = response.getEntity();
                if(entity != null){
                    String result = EntityUtils.toString(entity, "UTF-8");
                    jsonObject = JSONObject.fromObject(result);
                }
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            } catch (ClientProtocolException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
            return jsonObject;
        }
    
        /**
         * 获取access_token
         * @return
         */
        public static AccessToken getAccessToken(){
            AccessToken accessToken = new AccessToken();
            String url = ACCESS_TOKEN_URL.replace("APPID" ,APPID).replace("APPSECRET",APPSECRET);
            JSONObject jsonObject = doGetStr(url);
            if(jsonObject !=null){
                accessToken.setToken(jsonObject.getString("access_token"));
                accessToken.setExpireIn(jsonObject.getLong("expires_in"));
            }
            return accessToken;
        }

    测试是否成功获取:

    package com.hnu.scw.test;
    import com.hnu.scw.model.AccessToken;
    import com.hnu.scw.utils.WeiXinAccessTokenKeepAlive;
    import com.hnu.scw.utils.WeiXinUtils;
    import org.junit.Test;
    /**
     * @author scw
     * @create 2018-01-18 15:21
     * @desc 用于对Access_token内容相关的测试
     **/
    public class AccessTokenTest {
        /**
         * 获取到Access_Token,这个对于要想使用其他的微信接口,就必须要有这个进行验证
         */
        @Test
        public void getAccssTokenTest(){
            AccessToken accessToken = WeiXinUtils.getAccessToken();
            System.out.println("token:" +accessToken.getToken());
            System.out.println("有效时间:" +accessToken.getExpireIn());
        }
    }

    (五)自定义菜单

    下面的代码,就接着(四)中的类写就可以了,因为都属于微信开发的工具类

    步骤:

    (1)创建菜单按钮的实体对象类

    BaseButton:

    package com.hnu.scw.menu;
    /**
     * @author scw
     * @create 2018-01-17 17:20
     * @desc 最基础的Button
     **/
    public class BaseButton {
        private String type;
        private String name;
        //子按钮(也可以理解为二级菜单)
        private BaseButton[] sub_button;
    
        public String getType() {
            return type;
        }
    
        public void setType(String type) {
            this.type = type;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public BaseButton[] getSub_button() {
            return sub_button;
        }
    
        public void setSub_button(BaseButton[] sub_button) {
            this.sub_button = sub_button;
        }
    }
    

    clickButton:

    package com.hnu.scw.menu;
    /**
     * @author Administrator
     * @create 2018-01-17 17:21
     * @desc Click类型的Button实体
     **/
    public class ClickButton extends BaseButton {
        private String key;
    
        public String getKey() {
            return key;
        }
    
        public void setKey(String key) {
            this.key = key;
        }
    }

    viewButton:

    package com.hnu.scw.menu;
    /**
     * @author scw
     * @create 2018-01-17 17:22
     * @desc 类型是View的按钮实体
     **/
    public class ViewButton extends BaseButton{
        private String url;
    
        public String getUrl() {
            return url;
        }
    
        public void setUrl(String url) {
            this.url = url;
        }
    }
    

    CustomerMenu菜单定义:

    package com.hnu.scw.menu;
    /**
     * @author scw
     * @create 2018-01-17 17:23
     * @desc 自定义菜单的实体
     **/
    public class CustomeMenu {
        //对菜单按钮进行封装
        private BaseButton[] button;
    
        public BaseButton[] getButton() {
            return button;
        }
    
        public void setButton(BaseButton[] button) {
            this.button = button;
        }
    }

    (2)调用接口,进行菜单的创建

     /**
         * 设置菜单的形式
         * @return
         */
        public static CustomeMenu initMenu(){
            CustomeMenu customeMenu = new CustomeMenu();
            ClickButton clickButton = new ClickButton();
            clickButton.setName("click菜单");
            clickButton.setType("click");
            clickButton.setKey("01");
    
            ViewButton viewButton = new ViewButton();
            viewButton.setName("view菜单");
            viewButton.setType("view");
            viewButton.setUrl("需要访问的地址");
    
            ClickButton clickButton2 = new ClickButton();
            clickButton2.setName("扫码事件的click菜单");
            clickButton2.setType("scancode_push");
            clickButton2.setKey("02");
    
            ClickButton clickButton3 = new ClickButton();
            clickButton3.setName("地理位置的click菜单");
            clickButton3.setType("location_select");
            clickButton3.setKey("03");
    
            BaseButton baseButton = new BaseButton();
            baseButton.setName("菜单");
            //将clickButton2,clickButton3作为一个子菜单中的按钮
            baseButton.setSub_button(new BaseButton[]{clickButton2,clickButton3});
            //把按钮分别放入到菜单中
            customeMenu.setButton(new BaseButton[]{clickButton,viewButton,baseButton});
    
            return customeMenu;
        }
    
        /**
         * 创建自定义菜单
         * @param token
         * @param menu
         * @return
         */
        public static int createMenu(String token , String menu){
            int result = 0;
            String url = CREATE_MENU_URL.replace("ACCESS_TOKEN" ,token);
            JSONObject jsonObject = doPostStr(url, menu);
            if(jsonObject != null){
                //接受返回回来的参数,如果是0,就是创建成功
                result = jsonObject.getInt("errcode");
            }
            return result;
        }
    
        /**
         * 对菜单进行查询
         * @param token
         * @return
         */
        public static JSONObject queryMenu(String token){
            String url = QUERY_MENU_URL.replace("ACCESS_TOKEN" ,token);
            JSONObject jsonObject = doGetStr(url);
            return jsonObject;
        }
    
        /**
         * 对菜单进行删除
         * @param token
         * @return
         */
        public static JSONObject deleteMenu(String token){
            String url = DELETE_MENU_URL.replace("ACCESS_TOKEN" ,token);
            JSONObject jsonObject = doGetStr(url);
            return jsonObject;
        }

    (3)生成菜单

    package com.hnu.scw.test;
    import com.hnu.scw.model.AccessToken;
    import com.hnu.scw.utils.WeiXinUtils;
    import net.sf.json.JSONObject;
    import org.junit.Test;
    /**
     * @author scw
     * @create 2018-01-18 15:21
     * @desc 菜单相关的测试
     **/
    public class MenuTest {
        /**
         * 创建菜单
         */
        @Test
        public void creatMenuTest(){
            //获取到access_token
            AccessToken accessToken = WeiXinUtils.getAccessToken();
            //获取到自定义菜单的格式(JSONObject将对象转为json,然后再需要转为字符串型)
            String menu = JSONObject.fromObject(WeiXinUtils.initMenu()).toString();
            //调用创建菜单
            int result = WeiXinUtils.createMenu(accessToken.getToken(), menu);
            if(result == 0){
                //如果调用方法之后,返回的是0,那么就表示创建成功。
                System.out.println("创建成功");
            }else{
                System.out.println("创建失败");
            }
        }
        /**
         * 查询菜单
         */
        @Test
        public void queryMenuTest(){
            //获取到access_token
            AccessToken accessToken = WeiXinUtils.getAccessToken();
            //调用菜单查询的方法,返回是的一个Json格式
            JSONObject jsonObject = WeiXinUtils.queryMenu(accessToken.getToken());
            System.out.println(jsonObject);
        }
        /**
         * 删除菜单
         */
        @Test
        public void deleteMenuTest(){
            //获取到access_token
            AccessToken accessToken = WeiXinUtils.getAccessToken();
            //调用菜单查询的方法,返回是的一个Json格式
            JSONObject jsonObject = WeiXinUtils.deleteMenu(accessToken.getToken());
            if(jsonObject.getInt("errcode") == 0){
                //返回0,表示的是删除成功
                System.out.println("删除成功");
            }else{
                System.out.println("删除失败");
            }
    
        }
    }

           上面就是一些基本的微信公众号的交互了,刚刚接触可能不是很熟,慢慢的就了解了之后,其实也很简单的,当然,对于其中的一些高级接口的使用,可以看看我其他的文章,都有提到。。如果有问题,不明白的地方,欢迎进行交流和留言~!另外,推荐了比较好的学习资源,就是慕课网,这里面对于比较基础的微信公众号开发还是讲解的比较好,再结合我的文章,肯定是没有任何问题的。。。。

    Github的地址:

    https:https://github.com/qq496616246/WeChatCode.git

    git地址:git@github.com:qq496616246/WeChatCode.git

    展开全文
  • 微信开发——按钮

    2019-06-14 02:03:10
    2019独角兽企业重金招聘Python工程师标准>>> ...

    整体代码如下:

    package haust.vk.button;
    import java.io.BufferedReader;
    import java.io.File;
    import java.io.FileReader;
    import java.util.Map;
    import com.alibaba.fastjson.JSON;
    import haust.vk.utils.GetAccesstoken;
    import haust.vk.utils.UrlReq;
    /**
     * 按钮工具类  自定义也就是根据key来确定的 不再举例 和消息管理有重叠
     * @author viakiba
     *
     */
    public class ButtonUtils {
    	/**
    	 * 创建按钮
    	 * 		如果原来有按钮,可以在不删除的情况下更新按钮
    	 */
    	public static void createButton() {
    		// https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN
    		String accesstoken = GetAccesstoken.getAccesstoken();
    		String json = getButJson();
    		//按钮类型不再解释
    		String loadJson = UrlReq.loadJson("https://api.weixin.qq.com/cgi-bin/menu/create?access_token="+accesstoken, json);
    		Map map = JSON.parseObject(loadJson,Map.class);
    		Integer errcode =(Integer) map.get("errcode");
    		if(errcode == 0){
    			System.out.println("创建成功");
    		}else{
    			System.out.println("创建失败");
    		}
    	}
    	
    	
    	/**
    	 * 查询接口  https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN
    	 */
    	public static void selectButton() {
    		// https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN
    		String accesstoken = GetAccesstoken.getAccesstoken();
    		String json = getButJson();
    		//按钮类型不再解释
    		String loadJson = UrlReq.loadJson("https://api.weixin.qq.com/cgi-bin/menu/get?access_token="+accesstoken, json);
    		System.out.println(loadJson);
    	}
    	
    	
    	/**
    	 * 删除按钮
    	 * 
    	 */
    	public static void deleteButton() {
    		// https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN
    		String accesstoken = GetAccesstoken.getAccesstoken();
    		//按钮类型不再解释
    		String loadJson = UrlReq.loadJson("https://api.weixin.qq.com/cgi-bin/menu/delete?access_token="+accesstoken);
    		Map map = JSON.parseObject(loadJson,Map.class);
    		System.out.println(loadJson);
    		Integer errcode =(Integer) map.get("errcode");
    		if(errcode == 0){
    			System.out.println("删除成功");
    		}else{
    			System.out.println("删除失败");
    		}
    	}
    	
    	
    	
    	
    	
    	
    	/**
    	 * 辅助文件  拿到一个json数据
    	 * @return
    	 */
    	public static String getButJson(){
    		File file = new File("src/haust/vk/button/json.json");
    		StringBuffer sb = new StringBuffer();
    		
    		BufferedReader reader = null;
            try {
                
                reader = new BufferedReader(new FileReader(file));
                String tempString = null;
                // 一次读入一行,直到读入null为文件结束
                while ((tempString = reader.readLine()) != null) {
                    // 显示行号
                    sb.append(tempString);
                }
                reader.close();
            }catch(Exception e){
            	e.printStackTrace();
            }
    		return sb.toString();
    	}
    	
    	/**
    	 * 测试
    	 * @param args
    	 */
    	public static void main(String[] args) {
    		//createButton();
    		selectButton();
    		//deleteButton();
    	}
    }

    这里值得注意的是accesstoken ,有效期是两个小时,所以没有必要重复获取。而且新获取的会然原来获取的accesstoken 的失效。这样在高并发状态下会出问题的,所以根据微信的建议需要建立accesstoken 的中控。这样才能避免accesstoken 失效的问题。

    部分工具类在源码中都可以拿到

    代码下载:

    http://download.csdn.net/detail/meryhuang/9688173

    转载于:https://my.oschina.net/viakiba/blog/791144

    展开全文
  •  对于前端开发而言,微信小程序因为其简单快速、开发成本低、用户流量巨大等特点,也就成了前端开发工程师必会的一个技能。 2.先放上我做的小程序可以在微信小程序搜索“悬笔e绝”,或者用微信扫描下面的二维码哦...

    转自  https://www.cnblogs.com/xuanbiyijue/p/7980010.html

    一.写在前面

    1.为什么要学小程序开发?

        对于前端开发而言,微信小程序因为其简单快速、开发成本低、用户流量巨大等特点,也就成了前端开发工程师必会的一个技能。

     

    2.先放上我做的小程序

    可以在微信小程序搜索“悬笔e绝”,或者用微信扫描下面的二维码哦

     

     

     

    (1)欢迎页:这个logo是当年念大学给社团做的logo,苦学了整整一周的PS啊。。。

     

     

    (2)首页:轮播头图,天气,豆瓣电影正在热映

     

     

    (3)全国城市切换页

     

    (4)天气详情页

     

    (5)地图周边服务

     

    (6)豆瓣电影

     

     

    (7)热点新闻

     

    (8)更多页面

     

    3.开发准备:

    1)有人开玩笑说,会vue小程序根本都不用学:

    微信小程序虽然是腾讯自己搞的,但是核心的思想跟vue等框架是一样一样的哦~

    2)善于搜集精美的小组件: “我们不生产代码,我们只是代码的搬运工”,善于找到想要的组件并把他们巧妙优雅的组装成一个大项目,也算是程序员一项基本技能了。

    具体怎么找到想要的小程序demo,篇末会给大家推荐小程序的资源,有很多大神的项目哦

     

     

     

     撸起袖子开干了

     

    一.注册小程序账号,下载IDE

    1.官网注册https://mp.weixin.qq.com/,并下载IDE。

    2.官方文档一向都是最好的学习资料。

    注意:

    (1)注册账号之后会有一个appid,新建项目的时候需要填上,不然很多功能是用不了的,比如不能预览,不能上传代码等等。

    (2)如果你注册过微信公众号的话,一定要注意,微信公众号和小程序是两个账号,二者的appid也是不同,小程序开发必须使用小程序的appid哦。

     

    二.小程序框架介绍和运行机制

    1.我们建立了“普通快速启动模板”,然后整个项目目录如下:

    2.app.js

    整个项目的启动文件,如注释写的onlaunch方法有三大功能,浏览器缓存进行存和取数据;用登陆成功的回调;获取用户信息。

    globalData是定义整个项目的全局变量或者常量哦。

     

    3.app.json

    整个项目的配置文件,比如注册页面,配置tab页,设置整个项目的样式,页面标题等等;

    !注意:小程序启动默认的第一个页面,就是app.json的pages中的第一个页面哦。

    4.pages

    小程序的页面组件,有几个页面就会有几个子文件夹。比如快速启动模板,就有两个页面,index和logs

    5.打开index目录

    可以看到有三个文件,其实和我们web开发的文件是一一对应的。

    index.wxml对应index.html;

    index.wxss对应index.css;

    index.js就是js文件哦。

    一般我们还会给每个页面组件添加一个.json文件,作为该页面组件的配置文件,设置页面标题等功能

    6.双击index.js文件

    (1)var app = getApp(); 

    引入整个项目的app.js文件,用来取期中的公共变量等信息。

    如果要使用util.js工具库中的某个方法,在util.js中module.exports导出,然后在需要的页面中require即可得到哦。

    (2)比如,我们要获取豆瓣电影的时候,我们需要调用豆瓣的api;我们先在app.js中的gloabData中定义doubanBase

    然后在index.js中使用app.globaData.doubanBase即可取到这个值。

    当然这些常量你也可以在页面需要的时候,再用写死的值,但是为了整个项目的维护,还是建议把这种公用参数统一写在配置文件中哦。

     

    (3)接下来在整个page({})中,第一个data,就是本页面组件的内部数据,会渲染到该页面的wxml文件中,类似于vue、react哦~

    通过setData修改data数据,驱动页面渲染

    (4)一些生命周期函数

    比如onload(), onready(), onshow(), onhide()等等,监听页面加载、页面初次渲染、页面显示、页面隐藏等等

    更多的可以查官网API哦。其中用的最多的就是onload()方法,和onShareAppMessage()方法(设置页面分享的信息)

     

    7 .wxml模板的使用。

    比如本项目电影页面,就是以最小的星级评价组件wxml当做模板,star到movie到movie-list,一级一级的嵌套使用。

     star-template.wxml页面写好name属性;然后import引入的时候通过name获得即可

     

    8.常用的wxml标签

    view,text,icon,swiper,block,scroll-view等等,这些标签直接查官网文档即可

     

     

    三.小程序框架、各个页面以及发布上线的注意点

     

    1.整个框架中的一些注意点

    (1)整个wxml页面,最底层的标签是<page></page>哦。

    (2) 每个页面顶部导航栏的颜色,title在本页面的json中配置,如果没有配置的话,取app.json中的总配置哦。

    (3)json中不能写注释哦,不然会报错的。

    (4)路由相关

    1)使用wx.SwitchTab跳转tab页的话,在app.json中除了注册pages页面,还需要在tabBar中注册tab页,才能生效哦。

    注意:tab最多5个,也就是我们说的头部或者底部最多5个菜单。其他的页面只能通过其他路由方法打开哦。

    2)navigateTo是跳到某个非tab页,比如欢迎页,电影详情页,城市选择页;在app.json中注册后,不能在tabBar里注册哦,不然同样也是不能跳转的哦。

    3)reLaunch跳转,新开的页面左上角是没有退回按钮的,本项目只用了一次,切换城市的时候哦。

    (5)页面之间传递参数

    参数写在跳转的url之中,然后另一个页面在onload方法中的传参option接收到。如下传递和获取id

     

    (6)data-开头的自定义属性的使用

    比如wxml中我们怎么写 

    点击的事件对象可以这么取,var postId = event.currentTarget.dataset.postid;

    注意: 大写会转换成小写,带_符号会转成驼峰形式

    (7)事件对象event,event.target和event.currentTarget的区别:

    target指的是当前点击的组件 和currentTarget 指的是事件捕获的组件。

    比如,轮播图组件,点击事件应该要绑定到swiper上,这样才能监控任意一张图片是否被点击,

    这时target这里指的是image(因为点击的是图片),而currentTarget指的是swiper(因为绑定点击事件在swiper上)

    (8)使用免费的网络接口:

    本项目中用到了 和风天气api,腾讯地图api,百度地图api,豆瓣电影api,聚合头条新闻api等,具体用法可以看各自官网的接口文档哦,很详细的

    注意:免费接口是有访问限制的,所以如果用别人的组件用了这种接口的话,最好还是自己注册一个新的key替换上哦

    附上一个免费接口大全:

    https://github.com/jokermonn/-Api

    !!另外还要注意,要把这些接口的域名配置到小程序的合法域名中,不然也是访问不了的

    (8)wxss有一个坑:无法读取本地资源,比如背景图片用本地就会报错哦。

    把本地图片弄成网络图片的几种方式: 上传到个人网站;QQ空间相册等等也是可以的哦

     

     

    2.切换城市页面:

    (1)首页使用navigateTo跳转到切换城市页,由于首页并没有关闭,导致切换了城市返回来,天气信息还是旧的。

    正确的处理逻辑如下:

    1)使用reLaunch跳转到切换城市页面,实质是关闭所有页面打开新的页面哦。

    2)切换城市页面,更新公共变量中城市信息为手动切换的城区,再switchTab回到首页,触发首页重新加载。

    3)首页获取城市信息的时候加一个判断,全局没有才取定位的,全局有(比如刚才设置了)就用全局的哦。

    (2)城市列表的滚动和回到顶部

    基于scroll-view组件的scroll-top属性,初始就是0,滚动就会增加的;点击回到顶部给它置为0即可回到顶部

     

     

    3.天气页

    (1)初始化页面,天气显示的逻辑

    首先调用小程序的wx.getLocation方法获得当前的经纬度,然后调用腾讯地图获得当前的城市名称和区县名称,并存到公共变量中,

    再调用查询天气和空气质量的方法哦。

    (2)容错处理

    城市的名称长短不一,有点名字特别长,比如巴彦淖尔市这种,需要动态的获取完整的城市名称;

    有些偏僻的城市暂时没有天气信息,我们需要对返回的结果进行判断,没有信息的需要给用户一个良好的提示信息。

     

     

    4.周边-地图服务页面

    (1)调用百度地图的各种服务,查询酒店,美食,生活服务三种信息,更多信息可以看百度地图的文档

    (2)点击时给被点中的图标加个边框,数据驱动视图,所以使用一个长度为3的数组保存三个图标当前是否被点中的状态

    然后wxml再根据数据来动态添加class,增加边框样式

     

    5.豆瓣电影页

    (1)电影详情页的预览图片,用小程序本身的previewImage实现。

    (2)详情页使用onReachBottom()方法,监控用户上拉触底事件,然后发送请求继续获得数据,实现懒加载的效果

    (3)用户体验方面的优化,js中将整数评分比如7分统一改为7.0分,然后wxml模板再判断分数是否为0显示“暂无评分”

    (4)搜索之后清空搜索框

    因为小程序中不能使用getelementbyId这种方式获得元素,只能用数据来控制了

    在data中加一个属性searchText来保存搜索框的内容并和 input的value属性绑定,搜索完成或者点击X时,searchText变量清空即可实现清空输入框的效果哦。

     

    6.新闻页面

    (1)聚合头条新闻的免费接口,只返回了新闻的基本信息,新闻的主体内容是没有的哦。

    我找了好多新闻类的接口,好像都是没有新闻主体内容的。如果谁知道更好的接口欢迎留言告诉我哈~

    (2)当然,也可以自己去爬新闻网站的数据哦

     

    7.更多页面

     (1)小程序目前开放外链的功能只是给公司组织的小程序开放了,个人开发还是不能使用外链的哦。

     (2)彩蛋页面,获得用户信息

    通过 wx.setStorageSync('userInfos', userInfos);  可以获得登陆小程序的用户的个人信息,可以发送给后台存到数据库中,方便对用户进行分析

    我这里只是存储到浏览器缓存中哦,最大应该是10M缓存;如果用户把这个小程序从小程序列表中删除掉,就会清空这个缓存。

     

    8.发布注意

    (1) 新版本小程序发布的限制为2M,一般都是图片最占空间,所以尽量使用网络图片

    具体怎么把本地图片变成网络图片,上面有讲哦。

    (2)在开发者工具上预览测试没问题,点击上传;网页版小程序个的人中心的左侧“开发管理”菜单,第三块--开发版本就有了内容。

    (3)点击提交,填写小程序相关信息,就可以提交审核了哦。

    注意:分类最好填写准确,这样才能更快的通过审核哦。我这个小程序一天半时间过审上线的

     

     

    至此,我就把两天开发内碰到的坑和注意点都过了一遍,据说还有更多的坑,等之后更深入的开发再继续研究咯。

     

     

    四.写在最后

    1.推荐几个小程序开发的资料

    (1)知乎一篇小程序的资料:

    https://www.zhihu.com/question/50907897

    (2)小程序社区

    http://www.wxapp-union.com/portal.php

    (3)极乐小程序商店

    http://store.dreawer.com/

    2.我的个人微信公众号--悬笔e绝,欢迎关注哈~

    3.本项目的github地址,喜欢的童鞋点个star哈~

     https://github.com/yllg/WXxcx

     

    4.我的个人博客网站: 之后原创文章都会写在这里,然后不定期加一些酷炫的效果上去,嘿嘿嘿

    http://www.xuanbiyijue.com/

     

     

     

     

    展开全文
  •  开发环境参考:微信开发)--分享接口 点击打开链接 由于是基于weixin-java-tools 封装的java sdk,所以在微信底层处理上我们可以直接调用WxMpService. .数据准备: 进入页面前,根据条件查询相应的图片...

          今天整理一下微信开发中遇到的图片和附件的上传问题。

          开发环境参考:微信开发(一)--分享接口 点击打开链接

          由于是基于weixin-java-tools 封装的java sdk,所以在微信底层处理上我们可以直接调用WxMpService.

          .数据准备:

               进入页面前,根据条件查询相应的图片附件列表(已有数据时进行展示,没有数据可以忽略):

    	           //查询图片数据  
    		List<FileModel> imgList = fileModelService.loadFileModelsByBFTT(11);
    		   //查询附件数据
    		List<FileModel> attachmentFileList = fileModelService.loadFileModelsByBFTT(11);
    		

          二.页面准备:    

                jspye页面设置上传按钮,遍历读取图片列表   

        <!-- 图片上传 -->   
    				 <div class="form-group">
    				    <div class="input-group">
    				       <div class="input-group-addon "><span style="color:red;"> </span>图片上传:</div>
    				       <input type="hidden" name="fileIds" id="fileIds">
    				       <div class="input-group-addon">
    				       	
    				       				<a href="javascript:void(0)" οnclick="chooseImage('${basePath }','${id}');">
    						       			<span class="glyphicon glyphicon-plus"></span>
    						       		</a>
    				       
    				       </div>
    				    </div>
    				  </div>
    				  
    				  <!-- 缩略图 -->
    				  <ul class="body-img-ul">
    				  		<c:forEach items="${imgList}" var="v" varStatus="sta">
    				  			<li style="background-image:url('${simplePath }${v.revealpath }')" 
    				  				οnclick="previewImage('${simplePath }${v.revealpath }');">
    				  		
    					       		<i class="del_icon" οnclick="deleteImage('${v.id }',this,'${basePath }')"></i>
    		    	            </li>
    				  		</c:forEach>
    				  </ul>
    				   <!-- 附件上传 --> 
    				   <div class="form-group">
    				    <div class="input-group">
    				       <div class="input-group-addon "><span style="color:red;"> </span>附件上传:</div>
    				       <div class="input-group-addon">
    				       		<input type="hidden" name="attachmentIds" id="attachmentIds">
    				       		
    					       				<span class="glyphicon glyphicon-plus">
    					       					<input type="file" class="attachmentFile" name="attachmentFile" id="attachmentFile"
    						       				 οnchange="uploadAttachment('${basePath }','${id}');"/>
    					       				</span>
    				       </div>
    				    </div>
    				  </div>
    				  <!-- 附件 -->
    				  <ul class="body-file-ul">
    				  		<c:forEach items="${attachmentFileList }" var="v" varStatus="sta">
    		    	            <li>
    					  			<a href="${simplePath }${v.revealpath }">
    					  				<c:choose>
    			              		 		<c:when test="${fn:length(v.filename) < 20 }">
    			              		 			${v.filename }
    			              		 		</c:when>
    			              		 		<c:otherwise>
    			              		 			${fn:substring(v.filename,0,20) }....${v.extname }
    			              		 		</c:otherwise>
    			              		 	</c:choose>
    					  			</a>
    					
    					       				<i class="del_icon" οnclick="deleteImage('${v.id }',this,'${basePath }')"></i>
    		    	            </li>
    				  		</c:forEach>
    				  </ul>
    				<!-- 附件上传 -->  
    				    

         三.初始化jssdk:  

              controller

    @Controller
    @RequestMapping("jssdk")
    public class WeXinJsSdkController {
    	
    	@Autowired
    	private WxMpService wxMpService;
    	
    	@RequestMapping(value = "/config", method = RequestMethod.GET)
    	@ResponseBody
    	public WxJsapiSignature wxJsSdkConfig(HttpServletRequest request,String url) {
    		try {
    			WxJsapiSignature wxJsapiSignature = wxMpService.createJsapiSignature(url);
    			return wxJsapiSignature;
    		} catch (WxErrorException e) {
    			return null;
    		}
    	}
    	  
    }
      js请求获取config 参数 
    	/* 初始化jssdk */
    	$.get("${basePath}/jssdk/config.do",{url:window.location.href},function(data,status){
    		if(status == "success"){
    			wx.config({
    			    debug: false, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。
    			    appId: data.appId, // 必填,公众号的唯一标识
    			    timestamp: data.timestamp, // 必填,生成签名的时间戳
    			    nonceStr: data.nonceStr, // 必填,生成签名的随机串
    			    signature: data.signature,// 必填,签名,见附录1
    			    jsApiList: ['chooseImage', 'uploadImage', 'downloadImage', 'previewImage'] // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
    			 
    			});
    			wx.ready(function(){
    				//layer.msg("jssdk初始化成功");
    			    // config信息验证后会执行ready方法,所有接口调用都必须在config接口获得结果之后,config是一个客户端的异步操作,
    			    //所以如果需要在页面加载时就调用相关接口,则须把相关接口放在ready函数中调用来确保正确执行。对于用户触发时才调用的接口,则可以直接调用,不需要放在ready函数中。
    			});
    			wx.error(function(res){
    			    // config信息验证失败会执行error函数,如签名过期导致验证失败,具体错误信息可以打开config的debug模式查看,
    			    //也可以在返回的res参数中查看,对于SPA可以在这里更新签名。
    				layer.msg(res);
    			});
    		}
    		},"json");

      js 配置

        1. chooseImage    手机选择或拍摄图片 
        2. uploadImage    上传图片到微信服务器
        3. downloadImage  下载图片到本地

        previewImage   本地图片预览 


    	
    	//微信sdk上传图片------------------------
    var images = {
    	    localIds: [],
    	    serverIds: [],
    	  }; 
    function chooseImage(basePath,id){
    	wx.chooseImage({
    	    count: 6, // 默认9
    	    sizeType: ['original', 'compressed'], // 可以指定是原图还是压缩图,默认二者都有
    	    sourceType: ['album', 'camera'], // 可以指定来源是相册还是相机,默认二者都有
    	    success: function (res) {
    	    	images.localIds = res.localIds; // 返回选定照片的本地ID列表,localId可以作为img标签的src属性显示图片
    	    	//显示缩略图
    	        for(var i=0;i<images.localIds.length;i++){
    	        	var liLength = $(".body-img-ul li").length;
    	        	if(liLength >= 6 ){
    	        		layer.msg("最多允许上传6张图片!");
    					return;
    		        }
    	        	var imgHtml = "<li style=\"background-image:url('"+images.localIds[i]+"')\"";
    	        		   imgHtml += "οnclick=\"previewImage('"+images.localIds[i]+"')\">";
    	        		   imgHtml += "<i class=\"del_icon\"></i>";
    	        		   imgHtml += "</li>";
    	  				
    	        	$(".body-img-ul").append(imgHtml);
    		    }
    		    //递归的上传图片
    	        syncUpload(images.localIds,id);
    	    }
    	});
    }
    //将微信服务器的图片下载到本地
    var syncUpload = function(localIds,id){
    	var localId = localIds.pop();
    	//上传图片到微信服务器
        wx.uploadImage({
            localId: localId, // 需要上传的图片的本地ID,由chooseImage接口获得
            isShowProgressTips: 1, // 默认为1,显示进度提示
            success: function (res) {
            	images.serverIds.push(res.serverId); // 返回图片的服务器端ID
                if(localIds.length > 0){
                	syncUpload(localIds,id);  
                }else if(images.localIds.length == 0){
                	var serverids = images.serverIds.join(',');
                	$.post(
                		basePath+"/fileupload/uploadImg.do",
                		{"serverIds":serverids,"id":id}
                		,function(data,status){
            			if(status == "success"){
            				images.serverIds = [];
            				//存储文件id到页面
            				setFileIds(data.join(','));
            				
            				var tempLen = $(".del_icon").length;
            				var dataLen = data.length;
            				var del_index;
            				var del_id;
            				for(var i = 1;i<dataLen+1;i++){
            					del_index = tempLen-i;
            					del_id = data[i-1];
            					addDeleteEvent(del_index,del_id,basePath);
            				}
            				layer.msg("上传成功!");
            			}
            	  },"json");
                }
            }
        });
     }
    //添加事件
    function addDeleteEvent(del_index,del_id,basePath){
    	$(".del_icon").eq(del_index).click(function(e){
    		deleteImage(del_id,this,basePath);
    		window.event.cancelBubble = true;
    	});
    }
    //添加上传的filemodeID
    function setFileIds(fildIds){
    	var temp = $("#fileIds").val();
    	if(temp == ""){
    		 $("#fileIds").val(fildIds)
    	}else{
    		$("#fileIds").val(temp+","+fildIds)
    	}
    }
    //预览图片
    function previewImage(url){
    	document.write("url----"+url);
    	 wx.previewImage({
             current: url, // 当前显示图片的http链接
             urls: [url] // 需要预览的图片http链接列表
    	 });
    }
    //异步删除图片
    function deleteImage(id,obj,basePath){
    	$.ajax({
    		  type: 'POST',
    		  url: basePath+"/fileupload/deleteFile.do",
    		  data:{"id":id},
    		  dataType: 'json',
    		  success: function(data){
    			 if(data.state == 200){
    				layer.msg("删除成功!");
    				 //移除对应数据
    				 $(obj).parent()[0].remove();
    			 }else{
    				 layer.msg("删除失败!");
    			 }
    		  }
    		});
    	 window.event.cancelBubble = true;
    }
    //微信sdk上传图片------------------------
    //上传附件
    function uploadAttachment(basePath,id) {
       //限制数量
    	var liLength = $(".body-file-ul li").length;
    	if(liLength >= 6 ){
    		layer.msg("最多允许上传6份附件!");
    		return;
        }
       //上传文件
       $.ajaxFileUpload({
           url: basePath+"/fileupload/uploadAttachment.do", //文件上传到哪个地址,告诉ajaxFileUpload
           data:{"bussinessId":bussinessId,"id":id},
           secureuri: false, //一般设置为false
           fileElementId: 'attachmentFile', //文件上传控件的Id  <input type="file" id="fileUpload" name="file" />
           dataType: 'json', //返回值类型 一般设置为json
           success: function (data, status){//服务器成功响应处理函数
        	   if(data.state == 200){
        		   var imgHtml = "<li>";
        		   imgHtml += "<a href=\""+basePath+data.data[2]+"\">"+data.data[1]+"</a>";
        		   imgHtml += "<i class=\"del_icon\" οnclick=\"deleteImage('"+data.data[0]+"',this,'"+basePath+"')\"></i>";
        		   imgHtml += "</li>";
        		   $(".body-file-ul").append(imgHtml)
        		   //存储文件id到页面
        		   setFileIds(data.data[0]);
        	   }
    		   layer.msg(data.msg);
           	}
           });
       };
    	
    
    .后台配置文件名称,设置本地存储路径,把相应数据存储到本地:  

    @Controller
    @RequestMapping("/fileupload")
    public class FileUploadController {
    	
    	public String prefix ="yongjun/upload/";
    	private Logger logger = LoggerFactory.getLogger(this.getClass());
    	@Autowired
    	private WxMpService wxMpService;
    	@Autowired
    	private UsersService usersService;
    	@Autowired
    	private FileModelService fileModelService;
    	@Value(value = "#{resourceProperties['uploadUrl']}")
    	private String uploadUrl;
    	
    	@RequestMapping(value = "/uploadImg", method = RequestMethod.POST)
    	@ResponseBody
    	public String[] uploadImg(String serverIds,BigDecimal id,HttpServletRequest request){
    		
    		String[] result;
    		//获取当前用户
    		CurrentUser currentUser = usersService.getCurrentUser(request);
    		try {
    			String[] serverId_arr = serverIds.split(",");
    			if(serverId_arr != null && currentUser != null){
    				//图片存储
    				List<FileModel> fileModelStorelist = new ArrayList<FileModel>();
    				for (int i=0;i<serverId_arr.length;i++) {
    					File file = wxMpService.getMaterialService().mediaDownload(serverId_arr[i]);
    					String filePath = FileUploadUtil.uploadImg(file, uploadUrl, currentUser);
    					//文件模型数据
    					FileModel fileModel = new FileModel();
    					fileModel.setCreatedTime(new Date());
    					byte disabled = 0;
    					fileModel.setDisabled(disabled);
    					fileModel.setFilename(file.getName());
    					fileModel.setRealname(file.getName());
    					fileModel.setExtname(filePath.substring(filePath.lastIndexOf(".")+1));
    					fileModel.setFilepath(filePath);
    					fileModel.setRevealpath(filePath.replace("C:\\crm2009\\upload", "/myImg").replace("\\", "/"));
    					fileModel.setFiletype("image");
    					
    					fileModelStorelist.add(fileModel);
    				}
    				result = fileModelService.storeFileModelList(fileModelStorelist);
    				return result;
    			}else{
    				logger.info("当前用户不存在!!");
    				return null;
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    			logger.info("下载文件失败!!"+e.getClass());
    			return null;
    		}
    		
    		
    	}
    	@RequestMapping(value = "/uploadAttachment", method = RequestMethod.POST)
    	@ResponseBody
    	public ResultData uploadAttachment(BigDecimal bussinessId,BigDecimal id,HttpServletRequest request,MultipartFile attachmentFile){
    		
    		//判断文件类型
    		String originalFilename = attachmentFile.getOriginalFilename();
    		String[] imagType={".jpg",".png",".bmp",".gif"};
    		List<String> imagTypeList = Arrays.asList(imagType);
    		if(imagTypeList.contains(originalFilename.substring(originalFilename.lastIndexOf(".")).toLowerCase())){
    			return ResultData.error("无法上传图片!");
    		}
    		String[] result = new String[3];
    		//获取当前用户
    		CurrentUser currentUser = usersService.getCurrentUser(request);
    		try {
    			if(currentUser != null){
    				//上传文件
    				String filePath = FileUploadUtil.uploadFile(attachmentFile, uploadUrl, currentUser);
    				//文件模型数据
    				FileModel fileModel = new FileModel();
    				fileModel.setCreatedTime(new Date());
    				byte disabled = 0;
    				fileModel.setBussinessid(bussinessId);
    				fileModel.setDisabled(disabled);
    				fileModel.setFilename(originalFilename);
    				fileModel.setRealname(filePath.substring(filePath.lastIndexOf("/")+1));
    				fileModel.setExtname(filePath.substring(filePath.lastIndexOf(".")+1));
    				fileModel.setFilepath(filePath);
    				fileModel.setRevealpath(filePath.replace("C:\\crm2009\\upload", "/myImg").replace("\\", "/"));
    				fileModel.setFiletype("attachment");
    				fileModelService.storeFileModel(fileModel);
    				
    				result[0] = fileModel.getId().toString();
    				result[1] = fileModel.getFilename();
    				result[2] = fileModel.getRevealpath();
    				return ResultData.ok(result, "上传成功!");
    			}else{
    				logger.info("当前用户不存在!!");
    				return null;
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    			logger.info("下载文件失败!!"+e.getClass());
    			return null;
    		}
    		
    		
    	}
    	
    	@RequestMapping(value = "/deleteFile", method = RequestMethod.POST)
    	@ResponseBody
    	public ResultData deleteFile(BigDecimal id){
    		fileModelService.deleteFileModel(id);
    		return ResultData.ok();
    	}
    	
    }
    .设置 文件转接工具类:

         

    public class FileUploadUtil {
    	/**
    	 * 上传文件
    	 * @param file 源文件
    	 * @param uploadUrl 上传地址
    	 * @param currentUser 当前用户
    	 * @param fileType 
    	 * @return
    	 */
    	public  static String uploadImg(File file,String uploadUrl,CurrentUser currentUser) {
    		//处理待写入的位置
    		if(currentUser != null){
    			uploadUrl += "\\image\\" +currentUser.getLoginName().toString();
    		}
    		File uploadPosition = new File(uploadUrl);
    		if(!uploadPosition.exists()){
    			//文件路径不存在,则创建
    			uploadPosition.mkdirs();
    		}
    		if (file.renameTo(new File(uploadPosition + "\\" + file.getName()))) {
                System.out.println("File is moved successful!");
            } else {
                System.out.println("File is failed to move!");
            }
    		return uploadPosition + "/" + file.getName();
    	}
    	public  static String uploadFile(MultipartFile file,String uploadUrl,CurrentUser currentUser) throws UnsupportedEncodingException {
    		//处理待写入的位置
    		if(currentUser != null){
    			uploadUrl += "\\attachment\\" +currentUser.getLoginName().toString();
    		}
    		File uploadPosition = new File(uploadUrl);
    		if(!uploadPosition.exists()){
    			//文件路径不存在,则创建
    			uploadPosition.mkdirs();
    		}
    		String originalFilename = file.getOriginalFilename();
    		String realName = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
    		try {
    			file.transferTo(new File(uploadPosition + "\\" +  realName));
    			System.out.println("File is moved successful!");
    		} catch (IllegalStateException | IOException e) {
    			e.printStackTrace();
    			System.out.println("File is failed to move!");
    		}
    		return uploadPosition + "/" + realName;
    	}
    } 
       学习在于不断地探索、思考和总结记录,欢迎喜欢的朋友们在下方留言,与君共同进步!







    展开全文
  • 微信小程序中 input 的自定义删除按钮点不了 问题描述 业务场景:手机号输入框有个删除按钮可以删除输入的手机号。但是真机上经常输入框聚焦之后删除按钮点击不了。 问题原因 在开发者工具上调试发现:input 框...
  • 对于前端开发而言,微信小程序因为其简单快速、开发成本低、用户流量巨大等特点,也就成了前端开发工程师必会的一个技能。 &nbsp; 2.先看看小程序效果 (1)欢迎页 &nbsp; &nbsp; (2)首页:...
  • C#微信开发

    2017-12-24 10:35:37
    C#开发微信门户及应用教程   作者:伍华聪   C#开发微信门户及应用(1)--开始使用微信接口 6 1、微信账号 6 2、微信菜单定义 7 3、接入微信的链接处理 8 4、使用开发方式创建菜单 14 5、我创建的菜单案例 17 C#...
  • 微信公众号主要有以下几步骤 微信公众号的通讯机制 微信公众号简介 1.注册微信公众号 2.注册测试公众号 3.搭建微信本地调试环境 1)下载客户端natapp: 2)安装natapp: 4.微信公众号接入(校验签名) 第1步中...
  • 写在前面 服务号和订阅号URL配置创建... 最近公司在做微信开发,其实就是接口开发,网上找了很多资料,当然园友也写了很多教程,但都是理论说了大堆,实用指导或代码很少。如果你自己仔细研究下,其实就那么
  •  对于前端开发而言,微信小程序因为其简单快速、开发成本低、用户流量巨大等特点,也就成了前端开发工程师必会的一个技能。  2.开发准备:  (1)有人开玩笑说,会vue小程序根本都不用学:  微信小程序虽然是...
  • 微信开发 — 调用微信上传图片接口,并保存到自己的服务器 整体思路是这样的: 1.先把手机上的图片上传到微信服务器,然后返回一个图片ID 2.在通过后台根据ID从微信后台拿到流,保存到服务器 前几个步骤在之前的博客上有...
  • 微信公众平台开发问答是一个微信知识问答区,专注于提供微信应用及开发技术知识的整理、归类和检索。   主题:新手常见问题 问:我是新手,没有开发基础,应该如何学习微信公众平台的开发?答:先学习PHP和Mysql...
  • 微信公众号开发技术要点 微信公众号开发技术要点 微信公众号及其接口功能介绍 基本概念 公众号开发者模式 代码验证及图示 Open ID与Union ID 基本概念 使用说明 Access_token 基本介绍 注意事项 获取流程 ...
  • 本文节选自苏震巍撰写的《微信开发深度解析...MessageHandler 是一个微信消息的处理模块,也是整个微信开发过程中不可缺少的一部分。在 MessageHandler 中,开发者可以非常轻松地处理所有类型的微信消息。本文将介绍...
  • 微信打开开发者模式后,以前通过微信后台配置的菜单就失效了,需要通过我们自己服务器后台配置(需要开发),还有种比较简单的方法,就是通过微信提供的“微信公众平台接口测试工具”进行配置。下面重点说一下通过...
  • 原 快速开发一个自己的微信小程序 2018年07月21日 15:51:38 阅读数:300 ...
  • ① 文档介绍 : ② 验证程序 : ③ 部署web项目 : ④ 验证 :
  • 对于前端开发而言,微信小程序因为其简单快速、开发成本低、用户流量巨大等特点,也就成了前端开发工程师必会的一个技能。 2.开发准备: (1)有人开玩笑说,会vue小程序根本都不用学: 微信小程序虽然是腾讯自己...
  • 微信开发学习总结

    2016-07-11 12:52:18
    微信开发项目学习总结
1 2 3 4 5 ... 20
收藏数 13,623
精华内容 5,449