diff --git a/doc/web-weixin-api.md b/doc/web-weixin-api.md
index 0b13e62..5173074 100644
--- a/doc/web-weixin-api.md
+++ b/doc/web-weixin-api.md
@@ -158,7 +158,7 @@ URL参数分析:
FORM表单参数分析:
-|数名|示例值|描述|
+|参数名|示例值|描述|
|---|---|---|
|sid||登录时获取的;|
|uin||登录时获取的;|
@@ -996,3 +996,128 @@ POST参数分析:
```
https://file.wx.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json
```
+
+若文件较小,则只会产生一个该请求,若文件较大,则会产生多个该请求。多个请求表示以分片的方式发送文件数据,分片大小约为524288。
+
+FORM表单参数分析:
+
+|参数名|示例值|描述|
+|---|---|---|
+|~~id~~|WU_FILE_0|页面文件组件ID;非必须;|
+|~~name~~|bd00c8eed5c2cd522e69f38317b46903.jpg|文件名;非必须;|
+|~~type~~|image/jpeg|文件的MimeType;非必须;|
+|~~lastModifieDate~~||文件最后修改时间;非必须;|
+|~~size~~|99785|文件大小;非必须;|
+|chunks|3|文件分片数量;文件较大需要分片时必须;|
+|chunk|0|文件分片序号;从0开始;文件较大需要分片时必须;|
+|mediatype|pic|文件类型;图片为pic,其它文件为doc;非必须;|
+|uploadmediarequest||JSON字符串;必须;|
+|webwx_data_ticket||cookie中的数据;必须;|
+|pass_ticket||登录时获取的;必须;|
+|filename||文件二进制数据;必须;|
+
+uploadmediarequest结构示例:
+
+```json
+{
+ "UploadType": 2,
+ "BaseRequest": {
+ "Uin": 1245,
+ "Sid": "zxaODbK4ed",
+ "Skey": "@crypt_4f399da_3c7a87e93b7872bce",
+ "DeviceID": "e69688819413"
+ },
+ "ClientMediaId": 153686311,
+ "TotalLen": 87508,
+ "StartPos": 0,
+ "DataLen": 87508,
+ "MediaType": 4,
+ "FromUserName": "@6feb88ee01067121479f686",
+ "ToUserName": "@6feb88ee0106f67121479f686",
+ "FileMd5": "717d1dbb9833c7cdbdd09ac"
+}
+```
+
+服务端返回数据:
+
+```json
+{
+ "BaseResponse": {
+ "Ret": 0,
+ "ErrMsg": ""
+ },
+ "MediaId": "@crypt_17ab5.................39bd9cfa9ab",
+ "StartPos": 99785,
+ "CDNThumbImgHeight": 56,
+ "CDNThumbImgWidth": 100,
+ "EncryFileName": "bd00c8e.....38317b46903.jpg"
+}
+```
+
+上传成功后,返回数据中会有MediaId,如果是分片上传的,则最后一个请求会返回MediaId。
+
+### 4.4、上传媒体到服务器
+
+发送图片后,紧接着4.3的是如下POST请求:
+
+```
+https://wx2.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsgimg?fun=async&f=json&lang=zh_CN&pass_ticket=xFAjPcq0eXh.......TYHHdHgVL52eHo%253D
+```
+
+URL参数分析:
+
+|数名|示例值|描述|
+|---|---|---|
+|pass_ticket|xFAjPcq0eXh.......TYHHdHgVL52eHo%253D|同前面获取的;|
+
+POST的数据(JSON):
+
+```json
+{
+ "BaseRequest": {
+ "Uin": 16...245,
+ "Sid": "KvCP..../TNqh8",
+ "Skey": "@crypt_4fb399da......9f1b3d997c4b5eef82",
+ "DeviceID": "e2825...3652448"
+ },
+ "Msg": {
+ "Type": 3,
+ "MediaId": "@crypt_17ab5.................39bd9cfa9ab",
+ "Content": "",
+ "FromUserName": "@3641f45.......69bed57642",
+ "ToUserName": "@3641f454....9bed57642",
+ "LocalID": "153691....0527",
+ "ClientMsgId": "1536....390527"
+ },
+ "Scene": 0
+}
+```
+
+POST参数分析:
+
+|Key|示例值|描述|
+|---|---|---|
+|BaseRequest||同上;|
+|Msg.ClientMsgId||同LocalID;|
+|Msg.Content||消息内容;消息为媒体时,该值为媒体ID。|
+|Msg.FromUserName||发送用户;|
+|Msg.LocalID||13位时间戳+4位随机数;|
+|Msg.ToUserName||接收用户;|
+|Msg.Type|3|消息类型;同3.1中接收的消息类型;|
+|Msg.MediaId||4.3接口中返回的MediaId;|
+|Scene|0|固定值;|
+
+服务端返回数据:
+
+```json
+{
+ "BaseResponse": {
+ "Ret": 0,
+ "ErrMsg": ""
+ },
+ "MsgID": "28832......8291836",
+ "LocalID": "1536.....54390527"
+}
+```
+
+Web微信发送图片,需要先调4.3的接口将媒体文件上传到服务器,然后再调4.4的接口发送图片的相关信息。
diff --git a/pom.xml b/pom.xml
index 3baf1ab..ad631fe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
com.hotlcc
wechat4j
- 0.1.2
+ 0.2.0
wechat4j
Wechat client for Java.
diff --git a/src/main/java/com/hotlcc/wechat4j/Wechat.java b/src/main/java/com/hotlcc/wechat4j/Wechat.java
index 95ed37b..e490d41 100644
--- a/src/main/java/com/hotlcc/wechat4j/Wechat.java
+++ b/src/main/java/com/hotlcc/wechat4j/Wechat.java
@@ -6,10 +6,7 @@ import com.hotlcc.wechat4j.api.WebWeixinApi;
import com.hotlcc.wechat4j.enums.*;
import com.hotlcc.wechat4j.handler.ExitEventHandler;
import com.hotlcc.wechat4j.handler.ReceivedMsgHandler;
-import com.hotlcc.wechat4j.model.BaseRequest;
-import com.hotlcc.wechat4j.model.ReceivedMsg;
-import com.hotlcc.wechat4j.model.UserInfo;
-import com.hotlcc.wechat4j.model.WxMessage;
+import com.hotlcc.wechat4j.model.*;
import com.hotlcc.wechat4j.util.*;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
@@ -17,6 +14,7 @@ import org.apache.http.HttpRequestInterceptor;
import org.apache.http.client.CookieStore;
import org.apache.http.client.HttpClient;
import org.apache.http.cookie.Cookie;
+import org.apache.http.entity.ContentType;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.protocol.HttpContext;
@@ -1104,9 +1102,11 @@ public class Wechat {
/**
* 发送文本消息
*
+ * @param userName
+ * @param content
* @return
*/
- public JSONObject sendText(String content, String userName) {
+ public JSONObject sendText(String userName, String content) {
BaseRequest baseRequest = new BaseRequest(wxsid, skey, wxuin);
String msgId = WechatUtil.createMsgId();
@@ -1131,11 +1131,11 @@ public class Wechat {
/**
* 发送文本消息(根据昵称)
*
- * @param content
* @param nickName
+ * @param content
* @return
*/
- public JSONObject sendTextToNickName(String content, String nickName) {
+ public JSONObject sendTextToNickName(String nickName, String content) {
if (StringUtil.isEmpty(nickName)) {
return sendText(content, null);
}
@@ -1156,11 +1156,11 @@ public class Wechat {
/**
* 发送文本消息(根据备注名)
*
- * @param content
* @param remarkName
+ * @param content
* @return
*/
- public JSONObject sendTextToRemarkName(String content, String remarkName) {
+ public JSONObject sendTextToRemarkName(String remarkName, String content) {
if (StringUtil.isEmpty(remarkName)) {
return sendText(content, null);
}
@@ -1181,13 +1181,13 @@ public class Wechat {
/**
* 发送文本消息(根据多种名称)
*
- * @param content
* @param userName
* @param nickName
* @param remarkName
+ * @param content
* @return
*/
- public JSONObject sendText(String content, String userName, String nickName, String remarkName) {
+ public JSONObject sendText(String userName, String nickName, String remarkName, String content) {
UserInfo userInfo = null;
if (StringUtil.isNotEmpty(userName)) {
@@ -1211,15 +1211,196 @@ public class Wechat {
return sendText(content, userName);
}
- //TODO 待完成
- @Deprecated
- public JSONObject sendImage(File image, String userName) {
+ /**
+ * 发送图片消息
+ *
+ * @param userName
+ * @param mediaData
+ * @param mediaName
+ * @param contentType
+ * @return
+ */
+ public JSONObject sendImage(String userName, byte[] mediaData, String mediaName, ContentType contentType) {
String loginUserName = getLoginUserName(false);
String toUserName = StringUtil.isEmpty(userName) ? loginUserName : userName;
BaseRequest baseRequest = new BaseRequest(wxsid, skey, wxuin);
- String dataTicket = getCookieValue("webwx_data_ticket");
- JSONObject result = webWeixinApi.uploadMedia(httpClient, urlVersion, passTicket, baseRequest, loginUserName, toUserName, dataTicket, image);
+ // 上传媒体文件
+ String dataTicket = getCookieValue("webwx_data_ticket");
+ JSONObject result = webWeixinApi.uploadMedia(httpClient, urlVersion, passTicket, baseRequest, loginUserName, toUserName, dataTicket, mediaData, mediaName, contentType);
+ if (result == null) {
+ return null;
+ }
+ JSONObject BaseResponse = result.getJSONObject("BaseResponse");
+ if (BaseResponse == null) {
+ return result;
+ }
+ int Ret = BaseResponse.getIntValue("Ret");
+ if (Ret != 0) {
+ return result;
+ }
+
+ String MediaId = result.getString("MediaId");
+ if (StringUtil.isEmpty(MediaId)) {
+ return result;
+ }
+
+ // 发送图片消息
+ String msgId = WechatUtil.createMsgId();
+ MediaMessage message = new MediaMessage();
+ message.setClientMsgId(msgId);
+ message.setContent("");
+ message.setFromUserName(loginUserName);
+ message.setLocalID(msgId);
+ message.setMediaId(MediaId);
+ message.setToUserName(toUserName);
+ message.setType(MsgTypeEnum.IMAGE_MSG.getCode());
+ result = webWeixinApi.sendImageMsg(httpClient, urlVersion, passTicket, baseRequest, message);
+
return result;
}
+
+ /**
+ * 发送图片消息
+ *
+ * @param userName
+ * @param image
+ * @return
+ */
+ public JSONObject sendImage(String userName, File image) {
+ ContentType contentType = FileUtil.getContentBody(image);
+ byte[] mediaData = FileUtil.getBytes(image);
+ return sendImage(userName, mediaData, image.getName(), contentType);
+ }
+
+ /**
+ * 发送图片消息(根据昵称)
+ *
+ * @param nickName
+ * @param mediaData
+ * @param mediaName
+ * @param contentType
+ * @return
+ */
+ public JSONObject sendImageToNickName(String nickName, byte[] mediaData, String mediaName, ContentType contentType) {
+ if (StringUtil.isEmpty(nickName)) {
+ return sendImage(null, mediaData, mediaName, contentType);
+ }
+
+ UserInfo userInfo = getContactByNickName(false, nickName);
+ if (userInfo == null) {
+ return null;
+ }
+
+ String userName = userInfo.getUserName();
+ if (StringUtil.isEmpty(userName)) {
+ return null;
+ }
+
+ return sendImage(userName, mediaData, mediaName, contentType);
+ }
+
+ /**
+ * 发送图片消息(根据昵称)
+ *
+ * @param nickName
+ * @param image
+ * @return
+ */
+ public JSONObject sendImageToNickName(String nickName, File image) {
+ ContentType contentType = FileUtil.getContentBody(image);
+ byte[] mediaData = FileUtil.getBytes(image);
+ return sendImageToNickName(nickName, mediaData, image.getName(), contentType);
+ }
+
+ /**
+ * 发送图片消息(根据备注名)
+ *
+ * @param remarkName
+ * @param mediaData
+ * @param mediaName
+ * @param contentType
+ * @return
+ */
+ public JSONObject sendImageToRemarkName(String remarkName, byte[] mediaData, String mediaName, ContentType contentType) {
+ if (StringUtil.isEmpty(remarkName)) {
+ return sendImage(null, mediaData, mediaName, contentType);
+ }
+
+ UserInfo userInfo = getContactByRemarkName(false, remarkName);
+ if (userInfo == null) {
+ return null;
+ }
+
+ String userName = userInfo.getUserName();
+ if (StringUtil.isEmpty(userName)) {
+ return null;
+ }
+
+ return sendImage(userName, mediaData, mediaName, contentType);
+ }
+
+ /**
+ * 发送图片消息(根据备注名)
+ *
+ * @param remarkName
+ * @param image
+ * @return
+ */
+ public JSONObject sendImageToRemarkName(String remarkName, File image) {
+ ContentType contentType = FileUtil.getContentBody(image);
+ byte[] mediaData = FileUtil.getBytes(image);
+ return sendImageToRemarkName(remarkName, mediaData, image.getName(), contentType);
+ }
+
+ /**
+ * 发送图片消息(根据多种名称)
+ *
+ * @param userName
+ * @param nickName
+ * @param remarkName
+ * @param mediaData
+ * @param mediaName
+ * @param contentType
+ * @return
+ */
+ public JSONObject sendImage(String userName, String nickName, String remarkName
+ , byte[] mediaData, String mediaName, ContentType contentType) {
+ UserInfo userInfo = null;
+
+ if (StringUtil.isNotEmpty(userName)) {
+ return sendImage(userName, mediaData, mediaName, contentType);
+ } else if (StringUtil.isNotEmpty(nickName)) {
+ userInfo = getContactByNickName(false, nickName);
+ } else if (StringUtil.isNotEmpty(remarkName)) {
+ userInfo = getContactByRemarkName(false, remarkName);
+ } else {
+ String loginUserName = getLoginUserName(false);
+ return sendImage(loginUserName, mediaData, mediaName, contentType);
+ }
+
+ if (userInfo == null) {
+ return null;
+ }
+ userName = userInfo.getUserName();
+ if (StringUtil.isEmpty(userName)) {
+ return null;
+ }
+ return sendImage(userName, mediaData, mediaName, contentType);
+ }
+
+ /**
+ * 发送图片消息(根据多种名称)
+ *
+ * @param userName
+ * @param nickName
+ * @param remarkName
+ * @param image
+ * @return
+ */
+ public JSONObject sendImage(String userName, String nickName, String remarkName, File image) {
+ ContentType contentType = FileUtil.getContentBody(image);
+ byte[] mediaData = FileUtil.getBytes(image);
+ return sendImage(userName, nickName, remarkName, mediaData, image.getName(), contentType);
+ }
}
diff --git a/src/main/java/com/hotlcc/wechat4j/api/WebWeixinApi.java b/src/main/java/com/hotlcc/wechat4j/api/WebWeixinApi.java
index 4ede037..5e9b1e0 100644
--- a/src/main/java/com/hotlcc/wechat4j/api/WebWeixinApi.java
+++ b/src/main/java/com/hotlcc/wechat4j/api/WebWeixinApi.java
@@ -4,6 +4,7 @@ import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.hotlcc.wechat4j.enums.LoginTipEnum;
import com.hotlcc.wechat4j.model.BaseRequest;
+import com.hotlcc.wechat4j.model.MediaMessage;
import com.hotlcc.wechat4j.model.WxMessage;
import com.hotlcc.wechat4j.util.PropertiesUtil;
import com.hotlcc.wechat4j.util.StringUtil;
@@ -26,10 +27,9 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.stringtemplate.v4.ST;
-import javax.activation.MimetypesFileTypeMap;
-import java.io.File;
-import java.io.FileInputStream;
+import java.math.BigDecimal;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -43,13 +43,20 @@ import java.util.regex.Pattern;
public class WebWeixinApi {
private static Logger logger = LoggerFactory.getLogger(WebWeixinApi.class);
- //预编译正则匹配
+ /**
+ * 预编译正则匹配
+ */
private static Pattern PATTERN_UUID_1 = Pattern.compile("window.QRLogin.code = (\\d+);");
private static Pattern PATTERN_UUID_2 = Pattern.compile("window.QRLogin.code = (\\d+); window.QRLogin.uuid = \"(\\S+?)\";");
private static Pattern PATTERN_REDIRECT_URI_1 = Pattern.compile("window.code=(\\d+);");
private static Pattern PATTERN_REDIRECT_URI_2 = Pattern.compile("window.code=(\\d+);\\s*window.redirect_uri=\"(\\S+?)\";");
private static Pattern PATTERN_REDIRECT_URI_3 = Pattern.compile("http(s*)://wx(\\d*)\\.qq\\.com\\/");
+ /**
+ * 上传媒体文件分片大小
+ */
+ private static final int UPLOAD_MEDIA_FILE_CHUNK_SIZE = 524288;
+
/**
* 获取微信uuid
*/
@@ -312,7 +319,7 @@ public class WebWeixinApi {
try {
String url = new ST(PropertiesUtil.getProperty("webwx-url.webwxinit_url"))
.add("urlVersion", urlVersion)
- .add("pass_ticket", passticket)
+ .add("pass_ticket", StringUtil.encodeURL(passticket, Consts.UTF_8.name()))
.add("r", System.currentTimeMillis() / 1252L)
.render();
@@ -353,7 +360,7 @@ public class WebWeixinApi {
try {
String url = new ST(PropertiesUtil.getProperty("webwx-url.statusnotify_url"))
.add("urlVersion", urlVersion)
- .add("pass_ticket", passticket)
+ .add("pass_ticket", StringUtil.encodeURL(passticket, Consts.UTF_8.name()))
.render();
HttpPost httpPost = new HttpPost(url);
@@ -524,7 +531,7 @@ public class WebWeixinApi {
.add("urlVersion", urlVersion)
.add("skey", BaseRequest.getSkey())
.add("sid", BaseRequest.getSid())
- .add("pass_ticket", passticket)
+ .add("pass_ticket", StringUtil.encodeURL(passticket, Consts.UTF_8.name()))
.render();
HttpPost httpPost = new HttpPost(url);
@@ -563,7 +570,7 @@ public class WebWeixinApi {
try {
String url = new ST(PropertiesUtil.getProperty("webwx-url.webwxsendmsg_url"))
.add("urlVersion", urlVersion)
- .add("pass_ticket", passticket)
+ .add("pass_ticket", StringUtil.encodeURL(passticket, Consts.UTF_8.name()))
.render();
HttpPost httpPost = new HttpPost(url);
@@ -595,78 +602,18 @@ public class WebWeixinApi {
}
/**
- * 上传媒体文件
- *
- * @return
- */
- public JSONObject uploadMedia(HttpClient httpClient,
- String urlVersion,
- String passticket,
- BaseRequest BaseRequest,
- String FromUserName,
- String ToUserName,
- String dataTicket,
- byte[] data,
- String fileName,
- ContentType contentType) {
- try {
- String url = new ST(PropertiesUtil.getProperty("webwx-url.uploadmedia_url"))
- .add("urlVersion", urlVersion)
- .render();
-
- HttpPost httpPost = new HttpPost(url);
- httpPost.setHeader("Content-type", ContentType.MULTIPART_FORM_DATA.toString());
-
- long millis = System.currentTimeMillis();
-
- JSONObject uploadmediarequest = new JSONObject();
- uploadmediarequest.put("UploadType", 2);
- uploadmediarequest.put("BaseRequest", BaseRequest);
- uploadmediarequest.put("ClientMediaId", millis);
- uploadmediarequest.put("TotalLen", data.length);
- uploadmediarequest.put("StartPos", 0);
- uploadmediarequest.put("DataLen", data.length);
- uploadmediarequest.put("MediaType", 4);
- uploadmediarequest.put("FromUserName", FromUserName);
- uploadmediarequest.put("ToUserName", ToUserName);
- uploadmediarequest.put("FileMd5", DigestUtils.md5(data));
-
- HttpEntity paramEntity = MultipartEntityBuilder.create()
- .setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
- .addTextBody("id", StringUtil.getUuid(), ContentType.TEXT_PLAIN)
- .addTextBody("name", fileName, ContentType.TEXT_PLAIN)
- .addTextBody("type", contentType.getMimeType(), ContentType.TEXT_PLAIN)
- .addTextBody("lastModifieDate", millis + "", ContentType.TEXT_PLAIN)
- .addTextBody("size", data.length + "", ContentType.TEXT_PLAIN)
- .addTextBody("mediatype", WechatUtil.getMediatype(contentType.getMimeType()), ContentType.TEXT_PLAIN)
- .addTextBody("uploadmediarequest", uploadmediarequest.toJSONString(), ContentType.TEXT_PLAIN)
- .addTextBody("webwx_data_ticket", dataTicket, ContentType.TEXT_PLAIN)
- .addTextBody("pass_ticket", passticket, ContentType.TEXT_PLAIN)
- .addBinaryBody("filename", data, contentType, fileName)
- .build();
- httpPost.setEntity(paramEntity);
-
- HttpResponse response = httpClient.execute(httpPost);
- int statusCode = response.getStatusLine().getStatusCode();
- if (HttpStatus.SC_OK != statusCode) {
- throw new RuntimeException("响应失败(" + statusCode + ")");
- }
-
- HttpEntity entity = response.getEntity();
- String res = EntityUtils.toString(entity, Consts.UTF_8);
-
- JSONObject result = JSONObject.parseObject(res);
-
- return result;
- } catch (Exception e) {
- logger.error("上传媒体文件异常", e);
- return null;
- }
- }
-
- /**
- * 上传媒体文件
+ * 上传媒体文件(支持大文件自动分片上传)
*
+ * @param httpClient
+ * @param urlVersion
+ * @param passticket
+ * @param baseRequest
+ * @param fromUserName
+ * @param toUserName
+ * @param dataTicket
+ * @param mediaData
+ * @param mediaName
+ * @param contentType
* @return
*/
public JSONObject uploadMedia(HttpClient httpClient,
@@ -676,44 +623,113 @@ public class WebWeixinApi {
String fromUserName,
String toUserName,
String dataTicket,
- File file) {
+ byte[] mediaData,
+ String mediaName,
+ ContentType contentType) {
try {
String url = new ST(PropertiesUtil.getProperty("webwx-url.uploadmedia_url"))
.add("urlVersion", urlVersion)
.render();
+ long millis = System.currentTimeMillis();
+ int mediaLength = mediaData.length;
+ // String mimeType = contentType.getMimeType();
+
HttpPost httpPost = new HttpPost(url);
httpPost.setHeader("Content-type", ContentType.MULTIPART_FORM_DATA.toString());
- long millis = System.currentTimeMillis();
- String contentTypeStr = new MimetypesFileTypeMap().getContentType(file);
- ContentType contentType = ContentType.parse(contentTypeStr);
-
JSONObject uploadmediarequest = new JSONObject();
uploadmediarequest.put("UploadType", 2);
uploadmediarequest.put("BaseRequest", baseRequest);
uploadmediarequest.put("ClientMediaId", millis);
- uploadmediarequest.put("TotalLen", file.length());
+ uploadmediarequest.put("TotalLen", mediaLength);
uploadmediarequest.put("StartPos", 0);
- uploadmediarequest.put("DataLen", file.length());
+ uploadmediarequest.put("DataLen", mediaLength);
uploadmediarequest.put("MediaType", 4);
uploadmediarequest.put("FromUserName", fromUserName);
uploadmediarequest.put("ToUserName", toUserName);
- uploadmediarequest.put("FileMd5", DigestUtils.md5(new FileInputStream(file)));
+ uploadmediarequest.put("FileMd5", DigestUtils.md5Hex(mediaData));
- HttpEntity paramEntity = MultipartEntityBuilder.create()
- .setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
- .addTextBody("id", StringUtil.getUuid(), ContentType.TEXT_PLAIN)
- .addTextBody("name", file.getName(), ContentType.TEXT_PLAIN)
- .addTextBody("type", contentTypeStr, ContentType.TEXT_PLAIN)
- .addTextBody("lastModifieDate", millis + "", ContentType.TEXT_PLAIN)
- .addTextBody("size", file.length() + "", ContentType.TEXT_PLAIN)
- .addTextBody("mediatype", WechatUtil.getMediatype(contentType.getMimeType()), ContentType.TEXT_PLAIN)
- .addTextBody("uploadmediarequest", uploadmediarequest.toJSONString(), ContentType.TEXT_PLAIN)
- .addTextBody("webwx_data_ticket", dataTicket, ContentType.TEXT_PLAIN)
- .addTextBody("pass_ticket", passticket, ContentType.TEXT_PLAIN)
- .addBinaryBody("filename", file, contentType, file.getName())
- .build();
+ // 分片数量
+ int chunks = new BigDecimal(mediaLength).divide(new BigDecimal(UPLOAD_MEDIA_FILE_CHUNK_SIZE), 0, BigDecimal.ROUND_UP).intValue();
+
+ JSONObject result = null;
+ for (int chunk = 0; chunk < chunks; chunk++) {
+ int from = chunk * UPLOAD_MEDIA_FILE_CHUNK_SIZE;
+ int to = (chunk + 1) * UPLOAD_MEDIA_FILE_CHUNK_SIZE;
+ to = Math.min(to, mediaLength);
+ byte[] temp = Arrays.copyOfRange(mediaData, from, to);
+
+ HttpEntity paramEntity = MultipartEntityBuilder.create()
+ .setMode(HttpMultipartMode.BROWSER_COMPATIBLE)
+ // .addTextBody("id", StringUtil.getUuid(), ContentType.TEXT_PLAIN)
+ // .addTextBody("name", mediaName, ContentType.TEXT_PLAIN)
+ // .addTextBody("type", mimeType, ContentType.TEXT_PLAIN)
+ // .addTextBody("lastModifieDate", String.valueOf(millis), ContentType.TEXT_PLAIN)
+ // .addTextBody("size", String.valueOf(mediaLength), ContentType.TEXT_PLAIN)
+ .addTextBody("chunks", String.valueOf(chunks))
+ .addTextBody("chunk", String.valueOf(chunk))
+ // .addTextBody("mediatype", WechatUtil.getMediatype(mimeType), ContentType.TEXT_PLAIN)
+ .addTextBody("uploadmediarequest", uploadmediarequest.toJSONString(), ContentType.TEXT_PLAIN)
+ .addTextBody("webwx_data_ticket", dataTicket, ContentType.TEXT_PLAIN)
+ .addTextBody("pass_ticket", passticket, ContentType.TEXT_PLAIN)
+ .addBinaryBody("filename", temp, contentType, mediaName)
+ .build();
+ httpPost.setEntity(paramEntity);
+
+ HttpResponse response = httpClient.execute(httpPost);
+ int statusCode = response.getStatusLine().getStatusCode();
+ if (HttpStatus.SC_OK != statusCode) {
+ throw new RuntimeException("响应失败(" + statusCode + ")");
+ }
+
+ HttpEntity entity = response.getEntity();
+ String res = EntityUtils.toString(entity, Consts.UTF_8);
+
+ result = JSONObject.parseObject(res);
+ if (result == null) {
+ break;
+ }
+
+ JSONObject BaseResponse = result.getJSONObject("BaseResponse");
+ if (BaseResponse == null) {
+ break;
+ }
+ int Ret = BaseResponse.getIntValue("Ret");
+ if (Ret != 0) {
+ break;
+ }
+ }
+
+ return result;
+ } catch (Exception e) {
+ logger.error("上传媒体文件异常", e);
+ return null;
+ }
+ }
+
+ /**
+ * 发送图片消息
+ */
+ public JSONObject sendImageMsg(HttpClient httpClient,
+ String urlVersion,
+ String passticket,
+ BaseRequest baseRequest,
+ MediaMessage message) {
+ try {
+ String url = new ST(PropertiesUtil.getProperty("webwx-url.webwxsendmsgimg_url"))
+ .add("urlVersion", urlVersion)
+ .add("pass_ticket", StringUtil.encodeURL(passticket, Consts.UTF_8.name()))
+ .render();
+
+ HttpPost httpPost = new HttpPost(url);
+ httpPost.setHeader("Content-type", ContentType.APPLICATION_JSON.toString());
+
+ JSONObject paramJson = new JSONObject();
+ paramJson.put("BaseRequest", baseRequest);
+ paramJson.put("Msg", message);
+ paramJson.put("Scene", 0);
+ HttpEntity paramEntity = new StringEntity(paramJson.toJSONString(), Consts.UTF_8);
httpPost.setEntity(paramEntity);
HttpResponse response = httpClient.execute(httpPost);
@@ -729,7 +745,7 @@ public class WebWeixinApi {
return result;
} catch (Exception e) {
- logger.error("上传媒体文件异常", e);
+ logger.error("发送图片消息异常", e);
return null;
}
}
diff --git a/src/main/java/com/hotlcc/wechat4j/model/AppInfo.java b/src/main/java/com/hotlcc/wechat4j/model/AppInfo.java
index 780f828..40f17b3 100644
--- a/src/main/java/com/hotlcc/wechat4j/model/AppInfo.java
+++ b/src/main/java/com/hotlcc/wechat4j/model/AppInfo.java
@@ -5,11 +5,14 @@ import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Getter;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Getter
-public final class AppInfo {
+public final class AppInfo implements Serializable {
+ private static final long serialVersionUID = 1L;
+
private AppInfo() {
}
diff --git a/src/main/java/com/hotlcc/wechat4j/model/BaseRequest.java b/src/main/java/com/hotlcc/wechat4j/model/BaseRequest.java
index 9f77c74..be0883e 100644
--- a/src/main/java/com/hotlcc/wechat4j/model/BaseRequest.java
+++ b/src/main/java/com/hotlcc/wechat4j/model/BaseRequest.java
@@ -5,12 +5,16 @@ import com.hotlcc.wechat4j.util.WechatUtil;
import lombok.Getter;
import lombok.Setter;
+import java.io.Serializable;
+
/**
* 基本请求模型
*/
@Getter
@Setter
-public class BaseRequest {
+public class BaseRequest implements Serializable {
+ private static final long serialVersionUID = 1L;
+
public BaseRequest() {
}
diff --git a/src/main/java/com/hotlcc/wechat4j/model/MediaMessage.java b/src/main/java/com/hotlcc/wechat4j/model/MediaMessage.java
new file mode 100644
index 0000000..b925401
--- /dev/null
+++ b/src/main/java/com/hotlcc/wechat4j/model/MediaMessage.java
@@ -0,0 +1,15 @@
+package com.hotlcc.wechat4j.model;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import lombok.Getter;
+import lombok.Setter;
+
+/**
+ * 媒体消息
+ */
+@Getter
+@Setter
+public class MediaMessage extends WxMessage {
+ @JSONField(name = "MediaId")
+ private String mediaId;
+}
diff --git a/src/main/java/com/hotlcc/wechat4j/model/ReceivedMsg.java b/src/main/java/com/hotlcc/wechat4j/model/ReceivedMsg.java
index d5d56be..93a905d 100644
--- a/src/main/java/com/hotlcc/wechat4j/model/ReceivedMsg.java
+++ b/src/main/java/com/hotlcc/wechat4j/model/ReceivedMsg.java
@@ -5,11 +5,14 @@ import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Getter;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Getter
-public final class ReceivedMsg {
+public final class ReceivedMsg implements Serializable {
+ private static final long serialVersionUID = 1L;
+
private ReceivedMsg() {
}
diff --git a/src/main/java/com/hotlcc/wechat4j/model/RecommendInfo.java b/src/main/java/com/hotlcc/wechat4j/model/RecommendInfo.java
index bbc177d..269cfc5 100644
--- a/src/main/java/com/hotlcc/wechat4j/model/RecommendInfo.java
+++ b/src/main/java/com/hotlcc/wechat4j/model/RecommendInfo.java
@@ -5,11 +5,14 @@ import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Getter;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Getter
-public final class RecommendInfo {
+public final class RecommendInfo implements Serializable {
+ private static final long serialVersionUID = 1L;
+
private RecommendInfo() {
}
diff --git a/src/main/java/com/hotlcc/wechat4j/model/UserInfo.java b/src/main/java/com/hotlcc/wechat4j/model/UserInfo.java
index 6eba56a..d7dcced 100644
--- a/src/main/java/com/hotlcc/wechat4j/model/UserInfo.java
+++ b/src/main/java/com/hotlcc/wechat4j/model/UserInfo.java
@@ -5,6 +5,7 @@ import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import lombok.Getter;
+import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@@ -12,7 +13,9 @@ import java.util.List;
* 微信用户信息
*/
@Getter
-public final class UserInfo {
+public final class UserInfo implements Serializable {
+ private static final long serialVersionUID = 1L;
+
private UserInfo() {
}
diff --git a/src/main/java/com/hotlcc/wechat4j/model/WxMessage.java b/src/main/java/com/hotlcc/wechat4j/model/WxMessage.java
index 05db641..f6ffb89 100644
--- a/src/main/java/com/hotlcc/wechat4j/model/WxMessage.java
+++ b/src/main/java/com/hotlcc/wechat4j/model/WxMessage.java
@@ -4,12 +4,16 @@ import com.alibaba.fastjson.annotation.JSONField;
import lombok.Getter;
import lombok.Setter;
+import java.io.Serializable;
+
/**
* 要发送的消息
*/
@Getter
@Setter
-public class WxMessage {
+public class WxMessage implements Serializable {
+ private static final long serialVersionUID = 1L;
+
@JSONField(name = "ClientMsgId")
private String clientMsgId;
@JSONField(name = "Content")
diff --git a/src/main/java/com/hotlcc/wechat4j/util/FileUtil.java b/src/main/java/com/hotlcc/wechat4j/util/FileUtil.java
new file mode 100644
index 0000000..f9cb3a0
--- /dev/null
+++ b/src/main/java/com/hotlcc/wechat4j/util/FileUtil.java
@@ -0,0 +1,70 @@
+package com.hotlcc.wechat4j.util;
+
+import org.apache.http.entity.ContentType;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.activation.MimetypesFileTypeMap;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+
+/**
+ * 文件工具类
+ */
+public final class FileUtil {
+ private static Logger logger = LoggerFactory.getLogger(FileUtil.class);
+
+ private FileUtil() {
+ }
+
+ public static byte[] getBytes(File file) {
+ if (file == null) {
+ throw new IllegalArgumentException("参数file不能为null");
+ }
+
+ FileInputStream fis = null;
+ ByteArrayOutputStream baos = null;
+ try {
+ fis = new FileInputStream(file);
+ baos = new ByteArrayOutputStream();
+
+ byte[] buffer = new byte[1024];
+ int len;
+
+ while ((len = fis.read(buffer)) != -1) {
+ baos.write(buffer, 0, len);
+ }
+
+ baos.flush();
+ baos.close();
+ fis.close();
+
+ return baos.toByteArray();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ } finally {
+ if (fis != null) {
+ try {
+ fis.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ if (baos != null) {
+ try {
+ baos.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ public static ContentType getContentBody(File file) {
+ String mimeType = new MimetypesFileTypeMap().getContentType(file);
+ ContentType contentType = ContentType.parse(mimeType);
+ return contentType;
+ }
+}
diff --git a/src/main/resources/META-INF/wechat4j/webwx-url.properties b/src/main/resources/META-INF/wechat4j/webwx-url.properties
index f3176ac..cce8015 100644
--- a/src/main/resources/META-INF/wechat4j/webwx-url.properties
+++ b/src/main/resources/META-INF/wechat4j/webwx-url.properties
@@ -29,4 +29,6 @@ webwx-url.webwxsync_url=https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxs
## 4.2、发送消息
webwx-url.webwxsendmsg_url=https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsg?pass_ticket=
## 4.3、上传媒体文件
-webwx-url.uploadmedia_url=https://file.wx.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json
\ No newline at end of file
+webwx-url.uploadmedia_url=https://file.wx.qq.com/cgi-bin/mmwebwx-bin/webwxuploadmedia?f=json
+## 4.4、发送图片消息
+webwx-url.webwxsendmsgimg_url=https://wx.qq.com/cgi-bin/mmwebwx-bin/webwxsendmsgimg?fun=async&f=json&lang=zh_CN&pass_ticket=
\ No newline at end of file
diff --git a/src/test/java/TestClass.java b/src/test/java/TestClass.java
index 707b815..0a0cf5b 100644
--- a/src/test/java/TestClass.java
+++ b/src/test/java/TestClass.java
@@ -1,14 +1,22 @@
+import com.alibaba.fastjson.JSONObject;
import com.hotlcc.wechat4j.Wechat;
import com.hotlcc.wechat4j.api.WebWeixinApi;
import com.hotlcc.wechat4j.handler.ReceivedMsgHandler;
import com.hotlcc.wechat4j.model.ReceivedMsg;
import com.hotlcc.wechat4j.model.UserInfo;
import com.hotlcc.wechat4j.util.StringUtil;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.io.File;
public class TestClass {
- public static void main(String[] args) {
+ private Wechat wechat;
+
+ @Before
+ public void initAndLogin() {
+ wechat = new Wechat();
WebWeixinApi api = new WebWeixinApi();
- Wechat wechat = new Wechat();
wechat.setWebWeixinApi(api);
wechat.addReceivedMsgHandler(new ReceivedMsgHandler() {
@Override
@@ -18,6 +26,15 @@ public class TestClass {
System.out.println(name + ": " + msg.getContent());
}
});
+
wechat.autoLogin();
}
+
+ @Test
+ public void testSendImage() {
+ File file = new File("D:\\Downloads\\images\\6600e90b8b0ce2037a5291a7147ffd2b.jpeg");
+
+ JSONObject result = wechat.sendImage(null, file);
+ System.out.println(result);
+ }
}