JAVA微信公众号网页开发——将文章群发到微信公众号中(文章使用富文本,包含图片)
SendTextToAllUserAct.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 | package com.weixin.sendmessage; import org.apache.commons.lang.StringUtils; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.StatusLine; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.HttpResponseException; import org.apache.http.client.ResponseHandler; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; import org.json.JSONException; import org.json.JSONObject; import org.springframework.web.bind.annotation.RequestMapping; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import java.io.IOException; import java.net.URI; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 控制器 */ public class SendTextToAllUserAct { /** * 群发文章微信 * * @param ids 内容id * @return */ @RequestMapping ( "/content/o_sendToWeixin.do" ) public String sendToWeixin(Integer[] ids) { Content[] beans = new Content[ids.length]; for ( int i = 0 ; i < ids.length; i++) { beans[i] = contentMng.findById(ids[i]); //查询所有要发送的内容 } sendTextToAllUser(beans); return null ; } /** * 群发 */ public void sendTextToAllUser(Content[] beans) { String access_token = getToken(); //上传内容到微信 /** * 上传图文消息素材 * 官方接口文档:https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Batch_Sends_and_Originality_Checks.html#%E4%B8%8A%E4%BC%A0%E5%9B%BE%E6%96%87%E6%B6%88%E6%81%AF%E7%B4%A0%E6%9D%90%E3%80%90%E8%AE%A2%E9%98%85%E5%8F%B7%E4%B8%8E%E6%9C%8D%E5%8A%A1%E5%8F%B7%E8%AE%A4%E8%AF%81%E5%90%8E%E5%9D%87%E5%8F%AF%E7%94%A8%E3%80%91 */ String articalUploadUrl = "https://api.weixin.qq.com/cgi-bin/media/uploadnews" ; String url = articalUploadUrl + "?access_token=" + access_token; String[] str = articalUpload(access_token, beans); Integer contentCount = 0 ; contentCount = Integer.parseInt(str[ 1 ]); if (contentCount > 0 ) { HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); //HttpClient CloseableHttpClient client = httpClientBuilder.build(); client = (CloseableHttpClient) wrapClient(client); HttpPost post = new HttpPost(url); try { System.out.println( "str[0]:" + str[ 0 ]); StringEntity s = new StringEntity(str[ 0 ], "utf-8" ); s.setContentType( "application/json" ); post.setEntity(s); HttpResponse res = client.execute(post); HttpEntity entity = res.getEntity(); String contentString = EntityUtils.toString(entity, "utf-8" ); System.out.println( "contentString:" + contentString); JSONObject json = new JSONObject(contentString); //输出返回消息 String media_id = "" ; media_id = json.getString( "media_id" ); if (StringUtils.isNotBlank(media_id)) { /** *图文消息群发 * 官方接口文档:https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Batch_Sends_and_Originality_Checks.html */ String sendAllMessageUrl = "https://api.weixin.qq.com/cgi-bin/message/mass/sendall" ; String url_send = sendAllMessageUrl + "?access_token=" + token; String str_send = "{\"filter\":{\"is_to_all\":true},\"mpnews\":{\"media_id\":\"" + media_id + "\"},\"msgtype\":\"mpnews\"}" ; post(url_send, str_send, "application/json" ); } } catch (Exception e) { e.printStackTrace(); } } } private String post(String url, String json, String contentType) { HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); //HttpClient CloseableHttpClient client = httpClientBuilder.build(); client = (CloseableHttpClient) wrapClient(client); HttpPost post = new HttpPost(url); try { StringEntity s = new StringEntity(json, "utf-8" ); if (StringUtils.isBlank(contentType)) { s.setContentType( "application/json" ); } s.setContentType(contentType); post.setEntity(s); HttpResponse res = client.execute(post); HttpEntity entity = res.getEntity(); String str = EntityUtils.toString(entity, "utf-8" ); return str; } catch (Exception e) { e.printStackTrace(); } return null ; } /** * @param token * @param beans * @return */ private String[] articalUpload(String token, Content[] beans) { Integer count = 0 ; String str = "{\"articles\":[" ; for ( int i = 0 ; i < beans.length; i++) { //将富文本图片先上传到微信服务器上 String txt = contentHtmlProc(token, beans[i].getHtml()); String mediaId = "" ; if (!StringUtils.isBlank( "文章类型图" )) { String typeImg = "文章类型图" ; mediaId = uploadFile(token, typeImg, "image" ); System.out.println( "typeImg:" + typeImg); str = str + "{" + "\"thumb_media_id\":\"" + mediaId + "\"," + "\"author\":\"" + beans[i].getAuthor() + "\"," + "\"title\":\"" + beans[i].getTitle() + "\"," + "\"content_source_url\":\"" + beans[i].getSourceUrl() + "\"," + "\"content\":\"" + txt + "\"," + "\"digest\":\"" + beans[i].getDescription() + "\"," + "\"show_cover_pic\":\"0\"" + "}" ; if (i != beans.length - 1 ) { str = str + "," ; } count++; } } str = str + "]}" ; String[] result = new String[ 2 ]; result[ 0 ] = str; result[ 1 ] = count.toString(); return result; } /** * 把内容中的图片先上传到微信服务器 * * @param access_token 微信的access_token * @param txt 富文本内容 html代码 * @return */ private String contentHtmlProc(String access_token, String txt) { if (StringUtils.isBlank(txt)) { return "" ; } //从富文本的html中提取img图片路径 List<String> imgUrls = getImageSrc(txt); for (String img : imgUrls) { //img路径为图片的绝对路径 //上传图片到微信 String imgRealUrl = uploadImg(access_token, img); if (StringUtils.isNotBlank(imgRealUrl)) { txt = txt.replace(img, imgRealUrl); } } //html标签双引号需要注意 txt = txt.replaceAll( "\"" , "\'" ); return txt; } /** * 文件上传到微信服务器 * 官方接口文档:https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/New_temporary_materials.html * * @param access_token * @param filePath 文件路径 ,绝对地址 * @param type 文件类型 * @return */ public String uploadFile(String access_token, String filePath, String type) { String sendGetUrl = "https://api.weixin.qq.com/cgi-bin/media/upload" ; String url = sendGetUrl + "?access_token=" + access_token; String result = null ; String mediaId = "" ; FileUpload fileUpload = new FileUpload(); try { result = fileUpload.uploadFile(url, filePath, type); System.out.println( "result:" + result); if (result.startsWith( "{" ) && result.contains( "media_id" )) { JSONObject json = new JSONObject(result); mediaId = json.getString( "media_id" ); } } catch (Exception e) { e.printStackTrace(); } return mediaId; } /** * 把本地图片先上传到服务器 ,上传图文消息内的图片获取URL * 官网接口地址 :https://developers.weixin.qq.com/doc/offiaccount/Asset_Management/Adding_Permanent_Assets.html * * @param access_token * @param filePath 图片路径 * @return */ private String uploadImg(String access_token, String filePath) { String sendGetUrl = "https://api.weixin.qq.com/cgi-bin/media/uploadimg" ; String url = sendGetUrl + "?access_token=" + access_token; String result = null ; String mediaId = "" ; FileUpload fileUpload = new FileUpload(); try { result = fileUpload.uploadFile(url, filePath, null ); if (result.startsWith( "{" )) { JSONObject json = new JSONObject(result); mediaId = json.getString( "url" ); } } catch (Exception e) { e.printStackTrace(); } return mediaId; } /** * 提取img图片路径 * * @param htmlCode html代码 * @return */ public static List<String> getImageSrc(String htmlCode) { List<String> imageSrcList = new ArrayList<String>(); String regular = "<img(.*?)src=\"(.*?)\"" ; String img_pre = "(?i)<img(.*?)src=\"" ; String img_sub = "\"" ; Pattern p = Pattern.compile(regular, Pattern.CASE_INSENSITIVE); Matcher m = p.matcher(htmlCode); String src = null ; while (m.find()) { src = m.group(); src = src.replaceAll(img_pre, "" ).replaceAll(img_sub, "" ).trim(); imageSrcList.add(src); } return imageSrcList; } /** * 获取access_token * * @return */ public String getToken() { String tokenGetUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential" ;//微信提供获取access_token接口地址 String appid = "" ; String secret = "" ; System.out.println( "~~~~~appid:" + appid); System.out.println( "~~~~~secret:" + secret); JSONObject tokenJson = new JSONObject(); if (StringUtils.isNotBlank(appid) && StringUtils.isNotBlank(secret)) { tokenGetUrl += "&appid=" + appid + "&secret=" + secret; tokenJson = getUrlResponse(tokenGetUrl); System.out.println( "~~~~~tokenJson:" + tokenJson.toString()); try { return (String) tokenJson.get( "access_token" ); } catch (JSONException e) { System.out.println( "报错了" ); return null ; } } else { System.out.println( "appid和secret为空" ); return null ; } } private JSONObject getUrlResponse(String url) { CharsetHandler handler = new CharsetHandler( "UTF-8" ); try { HttpGet httpget = new HttpGet( new URI(url)); HttpClientBuilder httpClientBuilder = HttpClientBuilder.create(); //HttpClient CloseableHttpClient client = httpClientBuilder.build(); client = (CloseableHttpClient) wrapClient(client); return new JSONObject(client.execute(httpget, handler)); } catch (Exception e) { e.printStackTrace(); return null ; } } private static HttpClient wrapClient(HttpClient base) { try { SSLContext ctx = SSLContext.getInstance( "TLSv1" ); X509TrustManager tm = new X509TrustManager() { public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { } public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { } public X509Certificate[] getAcceptedIssuers() { return null ; } }; ctx.init( null , new TrustManager[]{tm}, null ); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(ctx, new String[]{ "TLSv1" }, null , SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER); CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory(sslsf).build(); return httpclient; } catch (Exception ex) { return null ; } } private class CharsetHandler implements ResponseHandler<String> { private String charset; public CharsetHandler(String charset) { this .charset = charset; } public String handleResponse(HttpResponse response) throws ClientProtocolException, IOException { StatusLine statusLine = response.getStatusLine(); if (statusLine.getStatusCode() >= 300 ) { throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase()); } HttpEntity entity = response.getEntity(); if (entity != null ) { if (!StringUtils.isBlank(charset)) { return EntityUtils.toString(entity, charset); } else { return EntityUtils.toString(entity); } } else { return null ; } } } } |
Content.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | package com.weixin.sendmessage; /** * 内容实体类 */ public class Content { private Integer id; private String author; private String html; private String title; private String description; private String sourceUrl; public String getSourceUrl() { return sourceUrl; } public void setSourceUrl(String sourceUrl) { this .sourceUrl = sourceUrl; } public Integer getId() { return id; } public void setId(Integer id) { this .id = id; } public String getAuthor() { return author; } public void setAuthor(String author) { this .author = author; } public String getHtml() { return html; } public void setHtml(String html) { this .html = html; } public String getTitle() { return title; } public void setTitle(String title) { this .title = title; } public String getDescription() { return description; } public void setDescription(String description) { this .description = description; } } |
FileUpload.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | package com.weixin.sendmessage; import org.apache.commons.lang.StringUtils; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSession; import java.io.*; import java.net.HttpURLConnection; import java.net.URL; public class FileUpload { /** * 模拟form表单的形式 ,上传文件 以输出流的形式把文件写入到url中,然后用输入流来获取url的响应 * * @param url 请求地址 form表单url地址 * @param filePath 文件在服务器保存路径 * @return String 正确上传返回media_id * @throws IOException */ /** 微信上传文件接口 */ public String uploadFile(String url, String filePath, String type) throws Exception { File file = new File(filePath); String result = null ; if (!file.exists() || !file.isFile()) { return "文件路径错误" ; } /** * 第一部分 */ if (StringUtils.isNotBlank(type)){ url = url+ "&type=" +type; } URL urlObj = new URL(url); HttpURLConnection con = null ; //解决HTTPS trustAllHttpsCertificates(); HostnameVerifier hv = new HostnameVerifier() { public boolean verify(String urlHostName, SSLSession session) { System.out.println( "Warning: URL Host: " + urlHostName + " vs. " + session.getPeerHost()); return true ; } }; HttpsURLConnection.setDefaultHostnameVerifier(hv); con=(HttpURLConnection) urlObj.openConnection(); /** * 设置关键值 */ con.setRequestMethod( "POST" ); // 以Post方式提交表单,默认get方式 con.setDoInput( true ); con.setDoOutput( true ); con.setUseCaches( false ); // post方式不能使用缓存 // 设置请求头信息 con.setRequestProperty( "Connection" , "Keep-Alive" ); con.setRequestProperty( "Charset" , "UTF-8" ); // 设置边界 String BOUNDARY = "----------" + System.currentTimeMillis(); con.setRequestProperty( "content-type" , "multipart/form-data; boundary=" + BOUNDARY); //con.setRequestProperty("Content-Type", "multipart/mixed; boundary=" + BOUNDARY); //con.setRequestProperty("content-type", "text/html"); // 请求正文信息 // 第一部分: StringBuilder sb = new StringBuilder(); sb.append( "--" ); // ////////必须多两道线 sb.append(BOUNDARY); sb.append( "\r\n" ); sb.append( "Content-Disposition: form-data;name=\"file\";filename=\"" + file.getName() + "\"\r\n" ); sb.append( "Content-Type:application/octet-stream\r\n\r\n" ); byte [] head = sb.toString().getBytes( "utf-8" ); // 获得输出流 OutputStream out = new DataOutputStream(con.getOutputStream()); out.write(head); // 文件正文部分 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(); /** * 读取服务器响应,必须读取,否则提交不成功 */ // con.getResponseCode(); /** * 下面的方式读取也是可以的 */ try { // 定义BufferedReader输入流来读取URL的响应 StringBuffer buffer = new StringBuffer(); BufferedReader reader = new BufferedReader( new InputStreamReader( con.getInputStream(), "UTF-8" )); String line = null ; while ((line = reader.readLine()) != null ) { //System.out.println(line); buffer.append(line); } if (result== null ){ result = buffer.toString(); } return buffer.toString(); } catch (Exception e) { System.out.println( "发送POST请求出现异常!" + e); e.printStackTrace(); } return result; } private static void trustAllHttpsCertificates() throws Exception { javax.net.ssl.TrustManager[] trustAllCerts = new javax.net.ssl.TrustManager[ 1 ]; javax.net.ssl.TrustManager tm = new miTM(); trustAllCerts[ 0 ] = tm; javax.net.ssl.SSLContext sc = javax.net.ssl.SSLContext .getInstance( "SSL" ); sc.init( null , trustAllCerts, null ); HttpsURLConnection.setDefaultSSLSocketFactory(sc .getSocketFactory()); } static class miTM implements javax.net.ssl.TrustManager, javax.net.ssl.X509TrustManager { public java.security.cert.X509Certificate[] getAcceptedIssuers() { return null ; } public boolean isServerTrusted( java.security.cert.X509Certificate[] certs) { return true ; } public boolean isClientTrusted( java.security.cert.X509Certificate[] certs) { return true ; } public void checkServerTrusted( java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException { return ; } public void checkClientTrusted( java.security.cert.X509Certificate[] certs, String authType) throws java.security.cert.CertificateException { return ; } } public static void main(String[] args) throws Exception { String filePath = "d:/mv1.jpg" ; String token= "Jdr_B5dQzbWlmmTAlMxbpOZiUfe100laWKeNjRgqfYAJ2GkgCdbQCQO4gAA6e0qd7uYM8fhhzx9ehQBCHlQvKQ" ; String result = null ; FileUpload fileUpload = new FileUpload(); result = fileUpload.uploadFile(token, filePath, "image" ); System.out.println(result); } } |
-----------------------有任何问题可以在评论区评论,也可以私信我,我看到的话会进行回复,欢迎大家指教------------------------
(蓝奏云官网有些地址失效了,需要把请求地址lanzous改成lanzoux才可以)
分类:
JAVA
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· DeepSeek 开源周回顾「GitHub 热点速览」
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了