1. 程式人生 > >微信開發者模式實現圖文訊息傳送

微信開發者模式實現圖文訊息傳送

公眾號型別

測試號

測試號現在僅支援使用預覽介面,並不能實現群發。

實現

基於springboot

前端是自己根據圖文訊息(Article)的格式設計的,Article的內容如下:

【圖片來源:官方文件截圖】

1. 上傳內容圖片獲取URL

【圖片來源:官方文件截圖】

前端使用bootstrap-fileinput上傳圖片,傳到後臺獲取URL,然後將URL作為圖片連結,放入wangEditor編輯器中。

(此處僅提供圖片素材上傳功能,暫未考慮視訊、音訊素材)

【注意:通過獲取的微信伺服器的URL插入編輯器時,可能會出現”未經允許“的圖案,只需在當前html中加入

<meta name="referrer" content="never">

即可】

後臺程式碼如下:

    @PostMapping(value = "/wx/upload/img")
    @ResponseBody
    public String uploadImg(MultipartFile img) throws IOException {
        JsonResult res = ResultUtil.success();
        String url = wxService.postImg(img);
        if (null != url) {
            res.setData(url);
        } else {
            res = ResultUtil.error(ResultEnum.UPLOAD_IMG_FAILURE);
        }
        return res.toString();
    }

     /**
     * 上傳圖文資訊圖片
     *
     * @param img 圖片
     * @return url
     */
    @Override
    @Nullable
    public String postImg(MultipartFile img) throws IOException {
        // 儲存到本地,獲取本地圖片絕對路徑
        String filePath = FileUtil.saveFile("TEMP", img, "TEMP_"+DataUtil.getTimeStringSixBit()+DataUtil.getRandom(6)+".jpg");
        JSONObject res = doPostForm(UPLOAD_IMG_URL.replace("ACCESS_TOKEN", wxTokenHelper.getToken().getToken()), filePath);
        return res.getString("url");
    }

    /**
     * POST請求 - Form格式資料
     *
     * @param url 請求地址
     * @param filePath 檔案本地路徑
     * @return
     */
    public JSONObject doPostForm(String url, String filePath) throws IOException {
        File file = new File(filePath);
        if (!file.exists() || !file.isFile()) {
            throw new IOException("檔案不存在");
        }

        URL urlObj = new URL(url);
        // 連線
        HttpURLConnection con = (HttpURLConnection) urlObj.openConnection();

        con.setRequestMethod("POST");
        con.setDoInput(true);
        con.setDoOutput(true);
        con.setUseCaches(false);

        // 設定請求頭資訊
        con.setRequestProperty("Connection", "Keep-Alive");
        con.setRequestProperty("Charset", "UTF-8");

        // 設定邊界
        String BOUNDARY = "----------" + System.currentTimeMillis();
        con.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY);

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

        byte[] head = sb.getBytes("utf-8");

        // 獲得輸出流
        OutputStream out = new DataOutputStream(con.getOutputStream());
        // 輸出表頭
        out.write(head);

        // 檔案正文部分
        // 把檔案已流檔案的方式 推入到url中
        DataInputStream in = new DataInputStream(new FileInputStream(file));
        int bytes = 0;
        byte[] bufferOut = new byte[1024];
        while ((bytes = in.read(bufferOut)) != -1) {
            out.write(bufferOut, 0, bytes);
        }
        in.close();

        // 結尾部分
        byte[] foot = ("\r\n--" + BOUNDARY + "--\r\n").getBytes("utf-8");// 定義最後資料分隔線

        out.write(foot);

        out.flush();
        out.close();

        StringBuilder buffer = new StringBuilder();
        BufferedReader reader = null;
        String result = null;
        try {
            // 定義BufferedReader輸入流來讀取URL的響應
            reader = new BufferedReader(new InputStreamReader(con.getInputStream()));
            String line = null;
            while ((line = reader.readLine()) != null) {
                buffer.append(line);
            }
            result = buffer.toString();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (reader != null) {
                reader.close();
            }
        }

        return JSONObject.parseObject(result);
    }

    

2. 上傳圖文訊息素材,得到media_id

【圖片來源:官方文件截圖】

     /**
     * 上傳圖文訊息素材
     *
     * @param articles 圖文訊息集合
     * @return media_id
     */
    @Override
    @Nullable
    public String postNews(Article[] articles) {
        JSONObject data = new JSONObject();
        data.put("articles", articles);
        JSONObject res = doPostStr(UPLOAD_NEWS_URL.replace("ACCESS_TOKEN", wxTokenHelper.getToken().getToken()), data.toJSONString());
        return res.getString("media_id");
    }

    /**
     * POST請求 - JSON格式資料
     *
     * @param url
     * @param outStr
     * @return
     */
    public JSONObject doPostStr(String url, String outStr) {
        HttpHeaders headers = new HttpHeaders();
        headers.set("Content-Type", "application/json;charset=UTF-8");// 解決請求亂碼問題
        String response = restTemplate.postForEntity(url, new HttpEntity<String>(outStr, headers), String.class)
                .getBody();
        return JSON.parseObject(response);
    }

    /**
 * @author kingsong
 * @create 2018/9/26 9:00
 * @desc 圖文訊息【推送】實體類
 **/
public class Article {

    private String thumb_media_id;  // 圖文訊息縮圖media_id

    private String author;  // 圖文訊息作者

    private String title;  // 圖文訊息標題

    private String content_source_url;  // “閱讀原文”連結,受安全限制,如需跳轉Appstore,可以使用itun.es或appsto.re的短鏈服務,並在短鏈後增加 #wechat_redirect 字尾。

    private String content;  // 訊息內容,支援HTML標籤

    private String digest;  // 推送描述,如本欄位為空,則預設抓去正文的前64個字

    private int show_cover_pic;  // 是否顯示封面

    public String getThumb_media_id() {
        return thumb_media_id;
    }

    public void setThumb_media_id(String thumb_media_id) {
        this.thumb_media_id = thumb_media_id;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getContent_source_url() {
        return content_source_url;
    }

    public void setContent_source_url(String content_source_url) {
        this.content_source_url = content_source_url;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public String getDigest() {
        return digest;
    }

    public void setDigest(String digest) {
        this.digest = digest;
    }

    public int getShow_cover_pic() {
        return show_cover_pic;
    }

    public void setShow_cover_pic(int show_cover_pic) {
        this.show_cover_pic = show_cover_pic;
    }

}

3. 傳送圖文訊息

測試號目前僅支援預覽介面

【圖片來源:官方文件截圖】

後臺程式碼如下:

    @PostMapping(value = "/wx/send/new")
    @ResponseBody
    public String sendNew(String media_id) throws Exception {
        JsonResult res = ResultUtil.success();
        res.setData(wxService.sendNewsByOpenIds(userService.getAllOpenIds(), media_id));
        return res.toString();
    }

    /**
     * 根OpenID群發圖文訊息
     *
     * @param openIds OpenID集合
     * @param media_id 圖文訊息媒體編號
     * @return msg_id
     * @throws Exception 傳送失敗
     */
    @Override
    public String sendNewsByOpenIds(List<String> openIds, String media_id) throws Exception {
        JSONObject data = new JSONObject();
        // 預覽介面僅支援一個openID
        data.put("touser", openIds.get(0));
        Map<String, String> npnews = new HashMap<>();
        npnews.put("media_id", media_id);
        data.put("mpnews", npnews);
        data.put("msgtype", "mpnews");
        data.put("send_ignore_reprint", 0);
        JSONObject res = doPostStr(SEND_NEWS_URL.replace("ACCESS_TOKEN", wxTokenHelper.getToken().getToken()), data.toJSONString());
        if(0!=res.getIntValue("errcode")) {
            // 傳送失敗
            throw new CustomException(ResultEnum.SEND_NEWS_FAILURE);
        } else {
            return res.getString("msg_id");
        }
    }