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); + } }