Skip to content

Commit dbb7afa

Browse files
authored
Merge pull request #403 from aaron7524/master
Java client 附件下载新功能实现
2 parents 0277270 + eac6334 commit dbb7afa

File tree

6 files changed

+355
-13
lines changed

6 files changed

+355
-13
lines changed

clients/java/wcf-bmc/src/main/java/com/wechat/ferry/controller/WeChatDllController.java

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.wechat.ferry.controller;
22

3+
import com.alibaba.fastjson2.JSONObject;
34
import java.util.List;
45

56
import org.springframework.beans.factory.annotation.Autowired;
@@ -14,6 +15,7 @@
1415
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseSqlReq;
1516
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseTableReq;
1617
import com.wechat.ferry.entity.vo.request.WxPpWcfDeleteGroupMemberReq;
18+
import com.wechat.ferry.entity.vo.request.WxPpWcfDownloadAttachReq;
1719
import com.wechat.ferry.entity.vo.request.WxPpWcfGroupMemberReq;
1820
import com.wechat.ferry.entity.vo.request.WxPpWcfInviteGroupMemberReq;
1921
import com.wechat.ferry.entity.vo.request.WxPpWcfPassFriendApplyReq;
@@ -41,10 +43,12 @@
4143
import com.wechat.ferry.entity.vo.response.WxPpWcfSendXmlMsgResp;
4244
import com.wechat.ferry.enums.ResponseCodeEnum;
4345
import com.wechat.ferry.service.WeChatDllService;
46+
import com.wechat.ferry.utils.PathUtils;
4447

4548
import io.swagger.annotations.Api;
4649
import io.swagger.annotations.ApiOperation;
4750
import lombok.extern.slf4j.Slf4j;
51+
import org.springframework.util.StringUtils;
4852

4953
/**
5054
* 控制层-微信DLL处理
@@ -258,16 +262,68 @@ public TResponse<Object> receiveTransfer(@Validated @RequestBody WxPpWcfReceiveT
258262
return TResponse.ok(ResponseCodeEnum.SUCCESS);
259263
}
260264

261-
// @ApiOperation(value = "下载图片、视频、文件", notes = "queryMsgTypeList")
262-
// @PostMapping(value = "/list/msgType")
263-
// public TResponse<Object> queryMsgTypeList() {
264-
// return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
265-
// }
266-
//
267-
// @ApiOperation(value = "解密图片", notes = "queryMsgTypeList")
268-
// @PostMapping(value = "/list/msgType")
269-
// public TResponse<Object> queryMsgTypeList() {
270-
// return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
271-
// }
265+
/**
266+
* 下载视频 add by wmz 2025-05-01
267+
*
268+
* @param request
269+
* @return
270+
* @throws Exception
271+
*/
272+
@ApiOperation(value = "下载视频", notes = "download_video")
273+
@PostMapping(value = "/download/video")
274+
public TResponse<Object> downloadVideo(@Validated @RequestBody WxPpWcfDownloadAttachReq request) throws Exception {
275+
String path = weChatDllService.downloadVideo(request);
276+
if (path != null) {
277+
JSONObject pathJson = new JSONObject();
278+
pathJson.put("path", path);
279+
return TResponse.ok(ResponseCodeEnum.SUCCESS, pathJson);
280+
}
281+
return TResponse.ok(ResponseCodeEnum.FAILED);
282+
}
283+
284+
/**
285+
* 下载图片 add by wmz 2025-05-02
286+
*
287+
* @param request
288+
* @return
289+
* @throws Exception
290+
*/
291+
@ApiOperation(value = "下载图片", notes = "download_picture")
292+
@PostMapping(value = "/download/picture")
293+
public TResponse<Object> downloadPicture(@Validated @RequestBody WxPpWcfDownloadAttachReq request) throws Exception {
294+
//check parameter
295+
String dir = request.getDir();
296+
if (!StringUtils.hasText(dir)) {
297+
log.info("需要指定图片的路径dir");
298+
return TResponse.fail("需要指定图片的路径dir");
299+
}
300+
boolean res = PathUtils.createDir(dir);
301+
if (!res) {
302+
return TResponse.fail("图片路径创建失败" + dir);
303+
}
304+
305+
String path = weChatDllService.downloadPicture(request);
306+
if (path != null) {
307+
JSONObject pathJson = new JSONObject();
308+
pathJson.put("path", path);
309+
return TResponse.ok(ResponseCodeEnum.SUCCESS, pathJson);
310+
}
311+
return TResponse.ok(ResponseCodeEnum.FAILED);
312+
}
313+
314+
/**
315+
* 暂未实现 add by mz 2025-05-01
316+
*
317+
* @param request
318+
* @return
319+
* @throws Exception
320+
*/
321+
@ApiOperation(value = "登陆二维码", notes = "loginQR")
322+
@PostMapping(value = "/loginQR")
323+
public TResponse<Object> loginQR(@Validated @RequestBody WxPpWcfDownloadAttachReq request) throws Exception {
324+
String path = weChatDllService.loginQR();
325+
return TResponse.ok(ResponseCodeEnum.SUCCESS, path);
326+
}
327+
272328

273329
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package com.wechat.ferry.entity.vo.request;
2+
3+
import io.swagger.annotations.ApiModel;
4+
import io.swagger.annotations.ApiModelProperty;
5+
import lombok.Data;
6+
7+
import javax.validation.constraints.NotBlank;
8+
9+
/**
10+
* 请求入参-下载附件信息
11+
*
12+
* @author wmz
13+
* @date 2025-05-02
14+
*/
15+
@Data
16+
@ApiModel(value = "wxPpWcfDownloadAttachReq", description = "个微WCF下载附件请求入参")
17+
public class WxPpWcfDownloadAttachReq {
18+
19+
/**
20+
* 消息接收人
21+
* 消息接收人,私聊为 wxid(wxid_xxxxxxxxxxxxxx)
22+
* 群聊为 roomid(xxxxxxxxxx@chatroom)
23+
*/
24+
@NotBlank(message = "消息id不能为空")
25+
@ApiModelProperty(value = "消息id")
26+
private Long id;
27+
28+
/**
29+
* 文件的extra
30+
*/
31+
@ApiModelProperty(value = "extra")
32+
private String extra;
33+
34+
/**
35+
* 缩略图thumb
36+
*/
37+
// @NotBlank(message = "thumb不能为空")
38+
@ApiModelProperty(value = "缩略图thumb")
39+
private String thumb;
40+
41+
/**
42+
* dir (str): 存放图片的目录。下载图片需要。暂不支持视频
43+
*/
44+
@ApiModelProperty(value = "图片存放路径dir")
45+
private String dir;
46+
47+
}

clients/java/wcf-bmc/src/main/java/com/wechat/ferry/handle/WeChatSocketClient.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,12 @@ public void connectRPC(String url, SDK INSTANCE) {
126126

127127
public Response sendCmd(Request req) {
128128
try {
129-
// 设置发送 20 秒超时
130-
cmdSocket.setSendTimeout(20000);
129+
// 不知道之前设置20S有啥特殊情况???这里设置超时时间 5s --> 参考Python版本
130+
// 防止无响应的时候线程一直阻塞--ReceiveTimeout
131+
// modify by wmz 2025-05-03
132+
cmdSocket.setSendTimeout(5000);
133+
cmdSocket.setReceiveTimeout(5000);
134+
131135
ByteBuffer bb = ByteBuffer.wrap(req.toByteArray());
132136
cmdSocket.send(bb);
133137
ByteBuffer ret = ByteBuffer.allocate(BUFFER_SIZE);
@@ -655,7 +659,9 @@ public int revokeMsg(Integer id) {
655659
* @param src 加密的图片路径
656660
* @param dir 保存图片的目录
657661
* @return 解密图片的保存路径
662+
* @see WeChatDllServiceImpl decryptImage
658663
*/
664+
@Deprecated
659665
public String decryptImage(String src, String dir) {
660666
Wcf.DecPath build = Wcf.DecPath.newBuilder().setSrc(src).setDst(dir).build();
661667
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_DECRYPT_IMAGE_VALUE).setDec(build).build();

clients/java/wcf-bmc/src/main/java/com/wechat/ferry/service/WeChatDllService.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseSqlReq;
77
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseTableReq;
88
import com.wechat.ferry.entity.vo.request.WxPpWcfDeleteGroupMemberReq;
9+
import com.wechat.ferry.entity.vo.request.WxPpWcfDownloadAttachReq;
910
import com.wechat.ferry.entity.vo.request.WxPpWcfGroupMemberReq;
1011
import com.wechat.ferry.entity.vo.request.WxPpWcfInviteGroupMemberReq;
1112
import com.wechat.ferry.entity.vo.request.WxPpWcfPassFriendApplyReq;
@@ -487,4 +488,39 @@ WxPpWcfSendRichTextMsgResp sendRichTextMsg(String recipient, String name, String
487488
*/
488489
String receiveTransfer(String weChatUid, String transferId, String transactionId);
489490

491+
/**
492+
* 下载视频文件
493+
*
494+
* @param request 请求入参
495+
* @return 文件路径
496+
*
497+
* @author wmz
498+
* @throws java.lang.Exception
499+
* @date 2025-05-02
500+
*/
501+
String downloadVideo(WxPpWcfDownloadAttachReq request) throws Exception;
502+
503+
/**
504+
* 下载图片
505+
*
506+
* @param request 请求入参
507+
* @return 文件路径
508+
*
509+
* @author wmz
510+
* @throws java.lang.Exception
511+
* @date 2025-05-02
512+
*/
513+
String downloadPicture(WxPpWcfDownloadAttachReq request) throws Exception;
514+
515+
/**
516+
* 获取登录二维码
517+
*
518+
* @return 文件路径
519+
*
520+
* @author wmz
521+
* @throws java.lang.Exception
522+
* @date 2025-05-02
523+
*/
524+
String loginQR() throws Exception;
525+
490526
}

clients/java/wcf-bmc/src/main/java/com/wechat/ferry/service/impl/WeChatDllServiceImpl.java

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseSqlReq;
2323
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseTableReq;
2424
import com.wechat.ferry.entity.vo.request.WxPpWcfDeleteGroupMemberReq;
25+
import com.wechat.ferry.entity.vo.request.WxPpWcfDownloadAttachReq;
2526
import com.wechat.ferry.entity.vo.request.WxPpWcfGroupMemberReq;
2627
import com.wechat.ferry.entity.vo.request.WxPpWcfInviteGroupMemberReq;
2728
import com.wechat.ferry.entity.vo.request.WxPpWcfPassFriendApplyReq;
@@ -56,6 +57,8 @@
5657
import com.wechat.ferry.handle.WeChatSocketClient;
5758
import com.wechat.ferry.service.WeChatDllService;
5859
import com.wechat.ferry.utils.HttpClientUtil;
60+
import com.wechat.ferry.utils.PathUtils;
61+
import java.io.File;
5962

6063
import lombok.extern.slf4j.Slf4j;
6164

@@ -762,5 +765,131 @@ private void checkClientStatus() {
762765
throw new BizException("微信客户端未登录或状态异常,请人工关闭本服务之后,退出微信客户端在重启本服务!");
763766
}
764767
}
768+
769+
770+
@Override
771+
public String loginQR() throws Exception {
772+
773+
long startTime = System.currentTimeMillis();
774+
// 公共校验
775+
checkClientStatus();
776+
log.info("[登录]-[获取二维码]-入参打印:{}", "");
777+
// # 强制等待 1 秒让数据入库,避免那帮人总是嗷嗷叫超时
778+
Thread.sleep(1000);
779+
//第一步
780+
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_REFRESH_QRCODE_VALUE)
781+
.build();
782+
Wcf.Response rsp = wechatSocketClient.sendCmd(req);
783+
rsp.getStatus();
784+
// RequestResponseBodyMethodProcessor s;
785+
long endTime = System.currentTimeMillis();
786+
log.info("[登录]-[获取二维码]-处理结束,耗时:{}ms", (endTime - startTime));
787+
System.out.println(rsp.getStr());
788+
return rsp.getStr();
789+
}
790+
791+
@Override
792+
public String downloadVideo(WxPpWcfDownloadAttachReq request) throws Exception {
793+
long startTime = System.currentTimeMillis();
794+
// 公共校验
795+
checkClientStatus();
796+
log.info("[下载]-[下载视频]-入参打印:{}", request);
797+
int status = attachDownload(request.getId(), "", request.getThumb());
798+
if (status != 0) {
799+
log.info("{}:下载视频出错", request.getId());
800+
return null;
801+
}
802+
//第二步,检测文件,指定下载的目录
803+
String base = PathUtils.removeExtension(request.getThumb());
804+
String filePath = base + ".mp4";
805+
String path = null;
806+
for (int i = 0; i < 30; i++) {
807+
if (new File(filePath).exists()) {
808+
path = filePath;
809+
log.info("视频下载完毕:{}", path);
810+
break;
811+
} else {
812+
log.info("等待下载中:{}", i);
813+
Thread.sleep(1000);
814+
}
815+
}
816+
long endTime = System.currentTimeMillis();
817+
log.info("[下载]-[下载视频]-处理结束,耗时:{}ms", (endTime - startTime));
818+
return path;
819+
}
820+
821+
@Override
822+
public String downloadPicture(WxPpWcfDownloadAttachReq request) throws Exception {
823+
long startTime = System.currentTimeMillis();
824+
// 公共校验
825+
checkClientStatus();
826+
log.info("[下载]-[下载图片]-入参打印:{}", request);
827+
int status = attachDownload(request.getId(), request.getExtra(), "");
828+
if (status != 0) {
829+
log.info("{}:下载出错", request.getId());
830+
return null;
831+
}
832+
//第二步解密图片--下载图片
833+
String filePath = decryptImage(request.getExtra(), request.getDir());
834+
String path = null;
835+
for (int i = 0; i < 15; i++) {
836+
if (new File(filePath).exists()) {
837+
log.info("图片下载完毕:{}", filePath);
838+
path = filePath;
839+
break;
840+
} else {
841+
log.info("等待下载中:{}", i);
842+
Thread.sleep(1000);
843+
}
844+
}
845+
long endTime = System.currentTimeMillis();
846+
log.info("[下载]-[下载图片]-处理结束,耗时:{}ms", (endTime - startTime));
847+
return path;
848+
}
849+
850+
/**
851+
* 下载附件(图片、视频、文件)。这方法别直接调用。
852+
*
853+
* @param id
854+
* @param extra
855+
* @param thumb
856+
* @return int: 0 为成功, 其他失败。
857+
* @throws Exception
858+
*/
859+
private int attachDownload(long id, String extra, String thumb) {
860+
try {
861+
//# 强制等待 1 秒让数据入库,避免那帮人总是嗷嗷叫超时
862+
Thread.sleep(1000);
863+
//第一步,下载
864+
Wcf.AttachMsg msg = Wcf.AttachMsg.newBuilder().setId(id)
865+
.setExtra(extra).setThumb(thumb)
866+
.build();
867+
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_DOWNLOAD_ATTACH_VALUE)
868+
.setAtt(msg)
869+
.build();
870+
Wcf.Response rsp = wechatSocketClient.sendCmd(req);
871+
return rsp.getStatus();
872+
} catch (Exception e) {
873+
e.printStackTrace();
874+
}
875+
return -1;
876+
}
877+
878+
/**
879+
* 解密图片
880+
*
881+
* @param srcPath 加密的图片路径
882+
* @param dir 保存图片的目录
883+
* @return 是否成功
884+
*/
885+
public String decryptImage(String srcPath, String dir) {
886+
Wcf.DecPath build = Wcf.DecPath.newBuilder().setSrc(srcPath).setDst(dir).build();
887+
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_DECRYPT_IMAGE_VALUE).setDec(build).build();
888+
Wcf.Response rsp = wechatSocketClient.sendCmd(req);
889+
if (rsp != null && rsp.getStatus() == 0) {
890+
return rsp.getStr();
891+
}
892+
return null;
893+
}
765894

766895
}

0 commit comments

Comments
 (0)