Java開發微信公衆號之被動回覆用戶消息-回覆圖片消息

一、前言

hello小夥伴們,大家好,做微信開發兩年了,最近看到微信方面的文章閱讀量和關注量上升的比較快速,激發了我滿滿的動力啊,所以就滋生了一個想法,從頭開始整理一下微信公衆號開發,寫一個簡易的教程,帶着大家實現一下微信開發者文檔上面一些使用比較多的功能,避免小夥伴們在接入微信開發後,不知道如何實現微信開發者文檔上面的一些API功能,今天帶來的主題是: SpringBoot+WxJava開發微信公衆號之圖片消息回覆,,如果不太瞭解被動回覆用戶文本信息的小夥伴,可以先看一下前面我寫過的一篇文章: Java開發微信公衆號之被動回覆用戶消息-回覆文本消息 , 廢話不多說,下面開始進入主題。

微信開發文檔: 微信官方文檔-被動回覆用戶消息

二、版本說明

  • spring boot.version: v2.1.7.RELEASE
  • java.version: 1.8
  • weixin-java-mp.version: 3.5.0

三、被動回覆消息

當用戶發送消息給公衆號時(或某些特定的用戶操作引發的事件推送時),會產生一個POST請求,開發者可以在響應包(Get)中返回特定XML結構,來對該消息進行響應(現支持回覆文本圖片圖文語音視頻音樂)。嚴格來說,發送被動響應消息其實並不是一種接口,而是對微信服務器發過來消息的一次回覆。

四、消息排重

微信服務器在將用戶的消息發給公衆號的開發者服務器URL地址(開發者中心處配置)後,微信服務器在五秒內收不到響應會斷掉連接,並且重新發起請求,總共重試三次,如果在Debug調試中,發現用戶無法收到響應的消息,可以檢查是否消息處理超時,一般debug調試的情況下,超過5秒就會收不到響應。關於重試的消息排重,每一次消息都應該保持其冪等性,在推送的xml數據結構中,有msgid的消息推薦使用msgid排重。如果是事件類型消息推薦使用FromUserName + CreateTime 進行排重。

五、如何保證五秒內處理並回復

假如服務器無法保證在五秒內處理並回復,必須做出下述回覆,這樣微信服務器纔不會對此作任何處理,並且不會發起重試(這種情況下,可以使用客服消息接口進行異步回覆),否則,將出現嚴重的錯誤提示。詳見下面說明:

1、直接回復success(推薦方式)
2、直接回復空串(指字節長度爲0的空字符串,而不是XML結構體中content字段的內容爲空)

一旦遇到服務器五秒內沒有做處理並且響應回覆或者開發者回覆了異常數據,比如JSON數據等;微信都會在公衆號會話中,向用戶下發系統提示“該公衆號暫時無法提供服務,請稍後再試”

六、如何回覆圖片消息

  1. 回覆圖片(不支持gif動圖)等多媒體消息時需要預先通過素材管理接口上傳臨時素材到微信服務器;也可以通過上傳素材管理接口中的永久素材到微信微服務器,只不過臨時素材在微信服務器上存儲的有效期只有3天,而永久素材永遠有效,由於微信永久素材的數量有上限,如果你的項目環境對對媒體素材文件需求量很大,謹慎使用永久素材上傳,使用臨時素材即可;不過也沒關係,微信開發者文檔也提供了刪除永久素材和刪除臨時素材的接口,根據場景合理使用即可。
  2. 通過素材管理接口上傳完臨時素材或者永久素材後,接口會返回一個關鍵字段叫做mediaId,這個mediaId返回給我們之後需要我們自己存儲記錄起來,等到需要使用多媒體文件素材的時候,都是通過傳遞mediaId到接口參數中,微信服務器會根據該mediaId去查找對應的素材文件,然後做對應的回覆處理,比如回覆圖片信息、回覆圖文信息等等。

七、預先上傳圖片素材

素材管理文檔: https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/Adding_Permanent_Assets.html

爲了小夥伴們方便使用,我直接將素材管理這一板塊的接口全部都做了一下封裝,封裝成一個工具類使用,小夥伴們在開發上傳素材相關功能時,直接拿我封裝的工具類調用即可了,下面貼出我封裝的工具類。

1. 素材管理工具類

  • WeChatMaterialUtils
package com.thinkingcao.weixin.manage;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.fs.FileUtils;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.WxMpMassNews;
import me.chanjar.weixin.mp.bean.WxMpMassOpenIdsMessage;
import me.chanjar.weixin.mp.bean.WxMpMassTagMessage;
import me.chanjar.weixin.mp.bean.material.*;
import me.chanjar.weixin.mp.bean.material.WxMpMaterialNews.WxMpMaterialNewsArticle;
import me.chanjar.weixin.mp.bean.material.WxMpMaterialNewsBatchGetResult.WxMaterialNewsBatchGetNewsItem;
import me.chanjar.weixin.mp.bean.result.WxMpMassUploadResult;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * <pre>
 * 微信素材管理 <br />
 * 注 : 封面圖需要用永久、富文本中圖片永久和臨時都可以、羣發圖文如果臨時用下就用臨時素材,否則用永久素材(大部分選永久)
 * @author cao_wencao
 * @date 2018年8月8日
 * </pre>
 */
@Slf4j
@Component
public class WeChatMaterialUtils {
    @Autowired
    private WxMpService wxMpService;

    private static Map<String, Map<String, Object>> mediaIds = Maps.newLinkedHashMap();

    /**
     * <pre>
     * 上傳羣發用的圖文消息,上傳後才能羣發圖文消息.
     * 
     * @see #massGroupMessageSend(WxMpMassTagMessage) 按分組羣發
     * @see #massOpenIdsMessageSend(WxMpMassOpenIdsMessage) 按openid羣發
     * @author cao_wencao
     * @param news
     * @return
     * </pre>
     */
    public WxMpMassUploadResult massNewsUpload(WxMpMassNews news) {
        // 圖文上傳
        WxMpMassUploadResult massUploadResult = null;
        try {
            massUploadResult = wxMpService.getMassMessageService().massNewsUpload(news);
            if (null != massUploadResult) {
                log.info("\n【上傳圖文素材】成功:  {}", massUploadResult.getMediaId());
                return massUploadResult;
            }
        }
        catch (Exception e) {
            log.error("\n【上傳圖文素材】失敗 :  {}", e.getMessage());
            e.printStackTrace();
        }
        return massUploadResult;

    }

    /**
     * <pre>
     * 上傳臨時多媒體文件 
     * 比如圖文消息的封面圖 圖片(image): 2M,支持PNG\JPEG\JPG\GIF格式
     * 語音(voice):2M,播放長度不超過60s,支持AMR\MP3格式 視頻(video):10MB,支持MP4格式
     * 縮略圖(thumb):64KB,支持JPG格式
     * 
     * @author cao_wencao
     * @param mediaType
     *            媒體文件類型,分別有圖片(image)、語音(voice)、視頻(video)和縮略圖(thumb)
     * @param fileType
     *            文件後綴
     * 
     * @param inputStream
     *            文件輸入流
     * @return type,media_id,created_at
     * </pre>
     */
    public WxMediaUploadResult mediaUpload(String mediaType, String fileType, InputStream inputStream) {
        if (null == inputStream || null == mediaType || null == fileType) {
            return null;
        }
        WxMediaUploadResult res = null;
        try {
            res = this.wxMpService.getMaterialService().mediaUpload(mediaType, fileType, inputStream);
            log.info("\n【多媒體文件上傳】成功MediaId : {} ", res.getMediaId());
            return res;
        }
        catch (WxErrorException e) {
            log.error("\n【多媒體文件上傳】失敗 : {}", e.getMessage());
            e.printStackTrace();
        }
        finally {
            if (null != inputStream) {
                try {
                    inputStream.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return res;
    }

    /**
     * <pre>
     * 上傳圖文消息內的圖片獲取URL(圖文消息裏面的圖片)
     * 
     * 請注意,本接口所上傳的圖片不佔用公衆號的素材庫中圖片數量的5000個的限制。圖片僅支持jpg/png格式,大小必須在1MB以下。
     * 詳情請見: <a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1444738729&token=&lang=zh_CN">新增永久素材</a>
     * 接口url格式:https://api.weixin.qq.com/cgi-bin/media/uploadimg?access_token=ACCESS_TOKEN
     * </pre>
     *
     * @param file
     *            上傳的文件對象
     * @return WxMediaImgUploadResult 返回圖片url
     * 
     */
    public WxMediaImgUploadResult uploadimg(File file) {
        if (null == file) {
            return null;
        }
        WxMediaImgUploadResult imageUploadResult = null;
        try {
            imageUploadResult = this.wxMpService.getMaterialService().mediaImgUpload(file);
            log.info("\n【上傳圖文消息內的圖片】成功  : {}", imageUploadResult.getUrl());
            return imageUploadResult;
        }
        catch (Exception e) {
            log.error("\n【上傳圖文消息內的圖片】失敗 : {}", e.getMessage());
            e.printStackTrace();
        }
        return imageUploadResult;
    }

    /**
     * <pre>
     * 獲取臨時素材
     * 公衆號可以使用本接口獲取臨時素材(即下載臨時的多媒體文件)。請注意,視頻文件不支持https下載,調用該接口需http協議。
     * 本接口即爲原“下載多媒體文件”接口。
     * 根據微信文檔,視頻文件下載不了,會返回null
     * 詳情請見: <a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1444738727&token=&lang=zh_CN">獲取臨時素材</a>
     * 接口url格式:https://api.weixin.qq.com/cgi-bin/media/get?access_token=ACCESS_TOKEN&media_id=MEDIA_ID
     * </pre>
     *
     * @param mediaId
     *            媒體文件Id
     * @return 保存到本地的臨時文件
     * @throws Exception
     */
    public File DownloadMaterial(String mediaId) {
        if (null == mediaId) {
            return null;
        }
        File file = null;
        try {
            file = this.wxMpService.getMaterialService().mediaDownload(mediaId);
            return file;
        }
        catch (Exception e) {
            log.error("\n【下載臨時素材】失敗 : {}", e.getMessage());
            e.printStackTrace();
        }
        return file;

    }

    // =======================================永久素材 :
    // 下載、上傳、刪除、更新====================================================

    /**
     * <pre>
     * 新增其他類型永久素材 非圖文永久素材上傳 
     * @author cao_wencao
     * @param mediaType
     *            媒體文件類型 :分別有圖片(image)、語音(voice)、視頻(video)和縮略圖(thumb)
     *            images支持 :bmp/png/jpeg/jpg/gif格式
     * @param fileType
     * @param fileName
     * @return WxMpMaterialUploadResult : "media_id":MEDIA_ID,  "url":URL(永久素材類型爲images纔會返回url)
     * </pre>
     */
    public WxMpMaterialUploadResult uploadFilesToWeChat(String mediaType, String fileType, String fileName, InputStream inputStream) {
        if (null == mediaType || null == fileType || null == inputStream || null == fileName) {
            return null;
        }
        WxMpMaterialUploadResult uploadResult = null;
        try {
            File tempFile = FileUtils.createTmpFile(inputStream, UUID.randomUUID().toString(), fileType);
            WxMpMaterial wxMaterial = new WxMpMaterial();
            wxMaterial.setFile(tempFile);
            wxMaterial.setName(fileName);
            // 視頻類永久素材需要在構造時傳入視頻名title和簡介introduction,目前已知視頻支持mp4格式。
            if (WxConsts.MediaFileType.VIDEO.equals(mediaType)) {
                wxMaterial.setVideoTitle("title");
                wxMaterial.setVideoIntroduction("test video description");
            }
            // 上傳素材至微信服務器
            uploadResult = wxMpService.getMaterialService().materialFileUpload(mediaType, wxMaterial);
            // 圖片纔會有url,其他均是media_id
            if (null != uploadResult && !StringUtils.isBlank(uploadResult.getMediaId())) {
                log.info("\n【上傳素材mediaType】 : {},{}成功!", uploadResult.getUrl(), uploadResult.getMediaId());
                return uploadResult;
            }
        }
        catch (WxErrorException e) {
            log.error("\n【上傳素材mediaType】: {}失敗 ", e.getMessage());
            e.printStackTrace();
        }
        catch (IOException e) {
            log.error("\n【上傳素材mediaType】: {}失敗 ", e.getMessage());
            e.printStackTrace();
        }
        return uploadResult;

    }

    /**
     * <pre>
     * 新增永久圖文素材 
     * 方法名: uploadNewsToWeChat 
     * 參數: WxMpMaterialNews 
     * 返回值:
     * WxMpMaterialUploadResult 圖文素材支持單圖文和多圖文,由類 WxMpMaterialNews 進行封裝,圖文的數據通過類
     * WxMpMaterialNews.WxMpMaterialNewsArticle 封裝。 多圖文消息在 WxMpMaterialNews 中調用
     * addArticle 方法添加多個 WxMpMaterialNewsArticle 對象即可。 接口返回 新增圖文消息的media_id
     * 
     * @author cao_wencao
     * @param materialNews
     *            要上傳的圖文消息的素材
     * @return String
     * </pre>
     */
    public WxMpMaterialUploadResult uploadNewsToWeChat(WxMpMaterialNews materialNews) {
        String media_id = null;
        WxMpMaterialUploadResult uploadResult = null;
        try {
            uploadResult = wxMpService.getMaterialService().materialNewsUpload(materialNews);
            if (null != uploadResult && !StringUtils.isBlank(uploadResult.getMediaId())) {
                media_id = uploadResult.getMediaId();
                log.info("\n【上傳圖文消息】成功:{}", media_id);
                return uploadResult;
            }
        }
        catch (Exception e) {
            log.error("\n【上傳圖文素材】失敗 : {}", e.getMessage());
            e.printStackTrace();
        }
        return uploadResult;
    }

    /**
     * <pre>
     * 圖文永久素材下載 根據 media_id 獲取圖文消息,
     * 結果由 WxMpMaterialNews 封裝,該類的結構和微信服務器返回的 json,
     * 格式數據的結構保持一致。
     * 
     * @author cao_wencao
     * @param newsMediaId
     *            要下載的圖文消息的id
     * @return WxMpMaterialNews
     * </pre>
     */
    public WxMpMaterialNews newsInfoDownload(String newsMediaId) {
        WxMpMaterialNews wxMpMaterialNews = null;
        try {
            wxMpMaterialNews = wxMpService.getMaterialService().materialNewsInfo(newsMediaId);
            log.info("\n【下載圖文素材】成功 : {}", wxMpMaterialNews.toJson().toString());
            return wxMpMaterialNews;
        }
        catch (Exception e) {
            log.error("\n【下載圖文素材】失敗 :{}", newsMediaId);
            e.printStackTrace();
        }
        return wxMpMaterialNews;
    }

    /**
     * <pre>
     * 永久素材刪除 根據 media_id 刪除素材
     * 
     * @author cao_wencao
     * @param mediaId
     *            要刪除的圖文消息的id
     * @return Boolean
     * </pre>
     */
    public Boolean DeleteMaterial(String mediaId) {
        Boolean result = false;
        try {
            result = wxMpService.getMaterialService().materialDelete(mediaId);
            log.info("\n【刪除永久素材】成功 : {} ;/n返回值 : {} ", mediaId, result);
            return result;
        }
        catch (Exception e) {
            log.error("\n【刪除永久素材】失敗 : {} ;/n返回值 : {} ", mediaId, result);
            e.printStackTrace();
        }
        return result;
    }

    /**
     * <pre>
     * 獲取圖文素材列表 並且下載
     * 詳情請見: <a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1444738734&token=&lang=zh_CN">獲取素材列表</a>
     * 接口url格式:https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token=ACCESS_TOKEN
     * @param offset
     *            從全部素材的該偏移位置開始返回,0表示從第一個素材 返回
     * @param count
     *            返回素材的數量,取值在1到20之間
     * </pre>
     */
    public WxMpMaterialNews materialNewsBatchGet(int offset, int count) throws Exception {
        if (offset < 0 || count <= 0) {
            return null;
        }
        WxMpMaterialNewsBatchGetResult newsBatchGetResult = null;
        WxMpMaterialNews wxMpMaterialNews = null;
        try {
            newsBatchGetResult = this.wxMpService.getMaterialService().materialNewsBatchGet(offset, count);
            if (newsBatchGetResult.getTotalCount() > 0 || newsBatchGetResult.getItemCount() > 0) {
                List<WxMaterialNewsBatchGetNewsItem> items = newsBatchGetResult.getItems();
                for (int i = 0; i < items.size(); i++) {
                    String mediaId = items.get(i).getMediaId();
                    if (StringUtils.isBlank(mediaId)) {
                        log.debug("【獲取微信圖文素材mediaId】失敗!");
                        return null;
                    }
                    // 調取圖文素材下載接口 ,參數 :mediaId
                    wxMpMaterialNews = newsInfoDownload(mediaId);
                }
            }
            if (newsBatchGetResult.getTotalCount() == 0 || newsBatchGetResult.getItemCount() == 0) {
                log.info("【獲取微信圖文素材列表】爲空!");
                return null;
            }
        }
        catch (Exception e) {
            log.error("\n【獲取微信圖文素材列表】失敗 : {}", e.getMessage());
            e.printStackTrace();
        }
        return wxMpMaterialNews;
    }

    /**
     * <pre>
     * 圖片或聲音永久素材下載
     * 根據 media_id 獲取聲音或者圖片的輸入流 
     * 
     * @author cao_wencao
     * @param mediaId
     *            要下載的圖文消息的id
     * @return InputStream
     * </pre>
     */
    public InputStream materialImageOrVoiceDownload(String mediaId) {
        if (null == mediaId) {
            return null;
        }
        InputStream inputStream = null;
        try {
            inputStream = wxMpService.getMaterialService().materialImageOrVoiceDownload(mediaId);
            if (null != inputStream) {
                log.info("\n【下載圖片或聲音永久素材】成功 : {}", mediaId);
                return inputStream;
            }
        }
        catch (Exception e) {
            log.error("\n【下載圖片或聲音永久素材】失敗 :{} \n媒體ID : {} ", e.getMessage(), mediaId);
            e.printStackTrace();
        }
        finally {
            if (null != inputStream) {
                try {
                    inputStream.close();
                }
                catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return inputStream;
    }

    /**
     * <pre>
     * 獲取視頻永久素材
     * 
     * 詳情請見: <a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1444738729&token=&lang=zh_CN">獲取永久素材</a>
     * 接口url格式:https://api.weixin.qq.com/cgi-bin/material/get_material?access_token=ACCESS_TOKEN
     * </pre>
     *
     * @param mediaId
     *            永久素材的id
     */
    public WxMpMaterialVideoInfoResult materialVideoInfo(String mediaId) {
        if (null == mediaId) {
            return null;
        }
        WxMpMaterialVideoInfoResult videoInfoResult = null;
        try {
            videoInfoResult = this.wxMpService.getMaterialService().materialVideoInfo(mediaId);
            if (null != videoInfoResult) {
                log.info("\n【下載視頻永久素材】成功 : {}", mediaId);
                return videoInfoResult;
            }
        }
        catch (Exception e) {
            log.error("\n【下載取視頻永久素材】失敗 : {} \n媒體ID : mediaId", e.getMessage(), mediaId);
            e.printStackTrace();
        }
        return videoInfoResult;
    }

    /**
     * <pre>
     * 分頁獲取其他媒體素材列表
     * @author cao_wencao
     * @param type    素材的類型,圖片(image)、視頻(video)、語音 (voice)、圖文(news)
     * @param offset  從全部素材的該偏移位置開始返回,0表示從第一個素材 返回
     * @param count   返回素材的數量,取值在1到20之間
     * @return
     */
    public WxMpMaterialFileBatchGetResult materialFileBatchGet(String type, int offset, int count) {
        if (null == type || offset < 0 || count <= 0) {
            return null;
        }
        WxMpMaterialFileBatchGetResult fileBatchGetResult = null;
        try {
            fileBatchGetResult = this.wxMpService.getMaterialService().materialFileBatchGet(type, offset, count);
            if (fileBatchGetResult.getTotalCount() < 0 || fileBatchGetResult.getItemCount() < 0) {
                log.error("【獲取微信圖文素材列表】失敗!");
            }
        }
        catch (Exception e) {
            log.error("\n【獲取微信圖文素材列表】失敗 : {}", e.getMessage());
            e.printStackTrace();
        }
        return fileBatchGetResult;

    }

    /**
     * <pre>
     * 修改永久圖文素材 根據更新圖文對象更新圖文素材,對於多圖文消息,如需更新其中某一個,需要設置更新對象中的index屬性
     * 
     * @author cao_wencao
     * @param newsMediaId
     *            要修改的圖文消息的id
     * @param index
     *            要更新的文章在圖文消息中的位置(多圖文消息時,此字段纔有意義),第一篇爲0
     * @param materialNews
     *            圖文消息內容及相關
     * @return
     * </pre>
     */
    public boolean UpdateNewsInfo(String newsMediaId, int index, WxMpMaterialNews materialNews) {
        // 獲取圖文永久素材的信息
        WxMpMaterialNews wxMpMaterialNews = null;
        try {
            wxMpMaterialNews = wxMpService.getMaterialService().materialNewsInfo(newsMediaId);
            log.info("\n【獲取圖文永久素材】成功 :{}", wxMpMaterialNews.toJson().toString());
        }
        catch (Exception e) {
            log.error("\n【獲取永久圖文素材】失敗 : {} ", newsMediaId);
            e.printStackTrace();
        }
        // 圖文素材更新實體類
        WxMpMaterialArticleUpdate wxMpMaterialArticleUpdate = new WxMpMaterialArticleUpdate();
        // dest article 目的article
        WxMpMaterialNews.WxMpMaterialNewsArticle article = new WxMpMaterialNewsArticle();
        // 更新一條單圖文
        List<WxMpMaterialNewsArticle> newsArticleList = materialNews.getArticles();
        // source article 來源article2
        WxMpMaterialNews.WxMpMaterialNewsArticle article2 = new WxMpMaterialNewsArticle();
        for (int i = 0; i < newsArticleList.size(); i++) {
            article2 = newsArticleList.get(i);
            article.setContent(article2.getContent());
            article.setAuthor(article2.getAuthor());
            article.setContentSourceUrl(article2.getContentSourceUrl());
            article.setDigest(article2.getDigest());
            article.setShowCoverPic(article2.isShowCoverPic());
            article.setThumbMediaId(newsMediaId);
            article.setNeedOpenComment(article2.getNeedOpenComment());
            article.setOnlyFansCanComment(article2.getOnlyFansCanComment());
            article.setThumbUrl(article2.getThumbUrl());
            article.setTitle(article2.getTitle());
            article.setUrl(article2.getUrl());
            wxMpMaterialArticleUpdate.setMediaId(newsMediaId);
            wxMpMaterialArticleUpdate.setArticles(article);
            wxMpMaterialArticleUpdate.setIndex(index);
        }
        boolean updateResult = false;
        try {
            updateResult = wxMpService.getMaterialService().materialNewsUpdate(wxMpMaterialArticleUpdate);
            log.info("\n【修改永久圖文素材】成功 : {} ", updateResult);
            return updateResult;
        }
        catch (Exception e) {
            log.error("\n【修改永久圖文素材】失敗 : {} ", updateResult);
            e.printStackTrace();
        }
        return updateResult;
    }

    /**
     * <pre>
     * 獲取圖文永久素材的信息
     * 
     * 詳情請見: <a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1444738729&token=&lang=zh_CN">獲取永久素材</a>
     * 接口url格式:https://api.weixin.qq.com/cgi-bin/material/get_material?access_token=ACCESS_TOKEN
     * </pre>
     *
     * @param mediaId
     *            永久素材的id
     */
    public WxMpMaterialNews getMaterialInfo(String mediaId) {
        WxMpMaterialNews wxMpMaterialNews = null;
        try {
            wxMpMaterialNews = wxMpService.getMaterialService().materialNewsInfo(mediaId);
            if (null != wxMpMaterialNews) {
                log.info("\n【從微信媒體庫獲取圖文永久素材】成功 :{}", wxMpMaterialNews.toJson().toString());
                return wxMpMaterialNews;
            }
        }
        catch (WxErrorException e) {
            log.error("\n【從微信媒體庫獲取永久圖文素材】異常 ,媒體ID :{}", mediaId);
            e.printStackTrace();
        }
        return null;
    }

    
    /**
     * <pre>
     * 獲取各類素材總數
     * 開發者可以根據本接口來獲取永久素材的列表,需要時也可保存到本地。
     * 請注意:
     *  1.永久素材的總數,也會計算公衆平臺官網素材管理中的素材
     *  2.圖片和圖文消息素材(包括單圖文和多圖文)的總數上限爲5000,其他素材的總數上限爲1000
     *  3.調用該接口需https協議
     * 
     * 詳情請見: <a href="http://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1444738733&token=&lang=zh_CN">獲取素材總數</a>
     * 接口url格式:https://api.weixin.qq.com/cgi-bin/material/get_materialcount?access_token=ACCESS_TOKEN
     *  "voice_count":COUNT, 語音總數量
     *  "video_count":COUNT, 視頻總數量
     *  "image_count":COUNT, 圖片總數量
     *  "news_count":COUNT   圖文總數量
     * </pre>
     */
    public String materialCount(String materiaType) {
        WxMpMaterialCountResult resultCount = null; //素材總數返回類
        String newsCount = null; // 圖文素材數量
        String imageCount = null;// 圖片素材數量
        try {
            resultCount = wxMpService.getMaterialService().materialCount();
            JSONObject jsonObject = (JSONObject) JSON.toJSON(resultCount);
            if (null != resultCount && WxConsts.MaterialType.IMAGE.equals(materiaType)) {
                imageCount = jsonObject.getString("imageCount"); // 圖片素材總數
                log.info("【獲取圖片總數成功,image_Count】:{}條", imageCount);
                return imageCount;
            }if (null != resultCount && WxConsts.MaterialType.NEWS.equals(materiaType)) {
                newsCount = jsonObject.getString("newsCount"); // 圖文素材總數
                log.info("【獲取圖文總數成功,news_Count】:{}條", newsCount);
                return newsCount;
            }
        }
        catch (WxErrorException e) {
            log.error("\n【獲取各類素材總數】異常 {}", e.getMessage());
            e.printStackTrace();
        }
        return null;

    }
}

2. 永久素材上傳Controller接口

  • WxMaterialController
package com.thinkingcao.weixin.controller;

import com.alibaba.fastjson.JSON;
import com.thinkingcao.weixin.manage.WeChatMaterialUtils;
import com.thinkingcao.weixin.result.ApiResult;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.material.WxMpMaterialUploadResult;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;

/**
 * @desc:
 * @author: cao_wencao
 * @date: 2020-05-21 13:47
 */
@Slf4j
@RestController
@RequestMapping("/wx/material")
public class WxMaterialController {

    @Autowired
    private WeChatMaterialUtils materialUtils;

    /**
     * 上傳單個圖片文件
     */
    @PostMapping("/uploadImg")
    public ApiResult uploadImg(@RequestParam("imgFile") MultipartFile imgFile) throws IOException {
        log.info("multipartFile = " + imgFile);
        log.info("ContentType = " + imgFile.getContentType());
        log.info("OriginalFilename = " + imgFile.getOriginalFilename());
        log.info("Name = " + imgFile.getName());
        log.info("Size = " + imgFile.getSize());
        String fileExt = imgFile.getOriginalFilename().substring(imgFile.getOriginalFilename().lastIndexOf(".") + 1);
        log.info("fileExt = " + fileExt);

        WxMpMaterialUploadResult wxMpMaterialUploadResult
                 = materialUtils.uploadFilesToWeChat("image", fileExt ,imgFile.getName(), imgFile.getInputStream());
        if(null == wxMpMaterialUploadResult){
            return ApiResult.error("上傳圖片失敗");
        }
        log.info("wxMpMaterialUploadResult = : " + JSON.toJSONString(wxMpMaterialUploadResult));
        return ApiResult.succee(wxMpMaterialUploadResult,"上傳圖片成功");
    }
}

3. 修改application.yml

#日誌配置
logging:
  level:
    org.springframework.web: INFO
    com.thinkingcao.weixin: DEBUG
    me.chanjar.weixin: DEBUG

#公衆號配置(可支持多公衆號配置,即list表示)
wx:
  mp:
    configs:
      - appId: wx041974352a21ff90
        secret: 2817d82c4ca63bcf32bc89ca5dd42077
        token: wechat
        aesKey: 111 (接口配置裏的EncodingAESKey值)

#配置項目訪問名稱
server:
  servlet:
    context-path: /springboot-wechat

4. 上傳圖片永久素材

使用PostMan工具,調用上傳永久素材多媒體文件的接口,如下:

在這裏插入圖片描述
IDEA控制檯日誌:
在這裏插入圖片描述

注: 調用上傳多媒體文件返回的mediaId我們需要保存起來,在後面回覆圖片消息的時候需要使用mediaId去微信服務器找對應的文件資源,由於我沒有集成mybatis+mysql,所以這裏就手動記錄下返回的mediaId,後面要用到,這裏mediaId = 1C72rnlYrj7ZqBiRGdKCoS54AXQwSo4iULd9qRhOC-U

八、定義ImgHandler

1. 路由初始化

WxMpConfiguration路由初始化類中注入ImgHandler圖片消息處理器,然後在messageRouter中加入路由規則定義,分別如下:

   private final ImgHandler imgHandler;
   // 圖片消息處理
   newRouter.rule().async(false).msgType(XmlMsgType.IMAGE).handler(this.imgHandler).end();

2.自定義image類型消息處理器

package com.thinkingcao.weixin.handler;

import me.chanjar.weixin.common.api.WxConsts;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.session.WxSessionManager;
import me.chanjar.weixin.mp.api.WxMpService;
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage;
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage;
import me.chanjar.weixin.mp.bean.result.WxMpUser;
import org.springframework.stereotype.Component;
import java.util.Map;

/**
 * @desc:
 * @author: cao_wencao
 * @date: 2020-05-21 14:16
 */
@Component
public class ImgHandler extends AbstractHandler{
    @Override
    public WxMpXmlOutMessage handle(WxMpXmlMessage wxMessage, Map<String, Object> map, WxMpService wxMpService, WxSessionManager wxSessionManager) throws WxErrorException {
        // msgType 消息類型
        String msgType = wxMessage.getMsgType();
        // content 消息內容
        String content = wxMessage.getContent();
        if (msgType.equals(WxConsts.XmlMsgType.IMAGE)){
            //TODO: 如果需要做微信消息日誌存儲,可以在這裏進行日誌存儲到數據庫,這裏省略不寫。
        }
        // 獲取微信用戶基本信息
        WxMpUser userWxInfo = wxMpService.getUserService().userInfo(wxMessage.getFromUser(), "zh_CN");
        if (null != userWxInfo){
            return WxMpXmlOutMessage
                    .IMAGE()
                    .mediaId("1C72rnlYrj7ZqBiRGdKCoS54AXQwSo4iULd9qRhOC-U")
                    .fromUser(wxMessage.getToUser())
                    .toUser(wxMessage.getFromUser())
                    .build();
        }
        return null;
    }
}

3. 實踐測試

在微信公衆號對話欄理輸入一個圖片文件,這時候會收到咱們在之前步驟上傳的圖片素材。

在這裏插入圖片描述

九、源碼

源碼: https://github.com/Thinkingcao/SpringBootLearning/tree/master/springboot-wechat

至此,使用SpringBoot+WxJava開發微信公衆號回覆圖片消息功能就講解到這裏了,這只是一個簡單的針對圖片消息的處理,回覆圖片消息功能實現,在實際的項目開發中做成數據庫動態的可配置的即可,這樣以來大大的提高了複用性。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章