commit
dbb7afad04
@ -1,5 +1,6 @@
|
||||
package com.wechat.ferry.controller;
|
||||
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
@ -14,6 +15,7 @@ import com.wechat.ferry.entity.vo.request.WxPpWcfAddFriendGroupMemberReq;
|
||||
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseSqlReq;
|
||||
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseTableReq;
|
||||
import com.wechat.ferry.entity.vo.request.WxPpWcfDeleteGroupMemberReq;
|
||||
import com.wechat.ferry.entity.vo.request.WxPpWcfDownloadAttachReq;
|
||||
import com.wechat.ferry.entity.vo.request.WxPpWcfGroupMemberReq;
|
||||
import com.wechat.ferry.entity.vo.request.WxPpWcfInviteGroupMemberReq;
|
||||
import com.wechat.ferry.entity.vo.request.WxPpWcfPassFriendApplyReq;
|
||||
@ -41,10 +43,12 @@ import com.wechat.ferry.entity.vo.response.WxPpWcfSendTextMsgResp;
|
||||
import com.wechat.ferry.entity.vo.response.WxPpWcfSendXmlMsgResp;
|
||||
import com.wechat.ferry.enums.ResponseCodeEnum;
|
||||
import com.wechat.ferry.service.WeChatDllService;
|
||||
import com.wechat.ferry.utils.PathUtils;
|
||||
|
||||
import io.swagger.annotations.Api;
|
||||
import io.swagger.annotations.ApiOperation;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* 控制层-微信DLL处理
|
||||
@ -258,16 +262,68 @@ public class WeChatDllController {
|
||||
return TResponse.ok(ResponseCodeEnum.SUCCESS);
|
||||
}
|
||||
|
||||
// @ApiOperation(value = "下载图片、视频、文件", notes = "queryMsgTypeList")
|
||||
// @PostMapping(value = "/list/msgType")
|
||||
// public TResponse<Object> queryMsgTypeList() {
|
||||
// return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
|
||||
// }
|
||||
//
|
||||
// @ApiOperation(value = "解密图片", notes = "queryMsgTypeList")
|
||||
// @PostMapping(value = "/list/msgType")
|
||||
// public TResponse<Object> queryMsgTypeList() {
|
||||
// return TResponse.ok(ResponseCodeEnum.SUCCESS, list);
|
||||
// }
|
||||
/**
|
||||
* 下载视频 add by wmz 2025-05-01
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@ApiOperation(value = "下载视频", notes = "download_video")
|
||||
@PostMapping(value = "/download/video")
|
||||
public TResponse<Object> downloadVideo(@Validated @RequestBody WxPpWcfDownloadAttachReq request) throws Exception {
|
||||
String path = weChatDllService.downloadVideo(request);
|
||||
if (path != null) {
|
||||
JSONObject pathJson = new JSONObject();
|
||||
pathJson.put("path", path);
|
||||
return TResponse.ok(ResponseCodeEnum.SUCCESS, pathJson);
|
||||
}
|
||||
return TResponse.ok(ResponseCodeEnum.FAILED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载图片 add by wmz 2025-05-02
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@ApiOperation(value = "下载图片", notes = "download_picture")
|
||||
@PostMapping(value = "/download/picture")
|
||||
public TResponse<Object> downloadPicture(@Validated @RequestBody WxPpWcfDownloadAttachReq request) throws Exception {
|
||||
//check parameter
|
||||
String dir = request.getDir();
|
||||
if (!StringUtils.hasText(dir)) {
|
||||
log.info("需要指定图片的路径dir");
|
||||
return TResponse.fail("需要指定图片的路径dir");
|
||||
}
|
||||
boolean res = PathUtils.createDir(dir);
|
||||
if (!res) {
|
||||
return TResponse.fail("图片路径创建失败" + dir);
|
||||
}
|
||||
|
||||
String path = weChatDllService.downloadPicture(request);
|
||||
if (path != null) {
|
||||
JSONObject pathJson = new JSONObject();
|
||||
pathJson.put("path", path);
|
||||
return TResponse.ok(ResponseCodeEnum.SUCCESS, pathJson);
|
||||
}
|
||||
return TResponse.ok(ResponseCodeEnum.FAILED);
|
||||
}
|
||||
|
||||
/**
|
||||
* 暂未实现 add by mz 2025-05-01
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
@ApiOperation(value = "登陆二维码", notes = "loginQR")
|
||||
@PostMapping(value = "/loginQR")
|
||||
public TResponse<Object> loginQR(@Validated @RequestBody WxPpWcfDownloadAttachReq request) throws Exception {
|
||||
String path = weChatDllService.loginQR();
|
||||
return TResponse.ok(ResponseCodeEnum.SUCCESS, path);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,47 @@
|
||||
package com.wechat.ferry.entity.vo.request;
|
||||
|
||||
import io.swagger.annotations.ApiModel;
|
||||
import io.swagger.annotations.ApiModelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotBlank;
|
||||
|
||||
/**
|
||||
* 请求入参-下载附件信息
|
||||
*
|
||||
* @author wmz
|
||||
* @date 2025-05-02
|
||||
*/
|
||||
@Data
|
||||
@ApiModel(value = "wxPpWcfDownloadAttachReq", description = "个微WCF下载附件请求入参")
|
||||
public class WxPpWcfDownloadAttachReq {
|
||||
|
||||
/**
|
||||
* 消息接收人
|
||||
* 消息接收人,私聊为 wxid(wxid_xxxxxxxxxxxxxx)
|
||||
* 群聊为 roomid(xxxxxxxxxx@chatroom)
|
||||
*/
|
||||
@NotBlank(message = "消息id不能为空")
|
||||
@ApiModelProperty(value = "消息id")
|
||||
private Long id;
|
||||
|
||||
/**
|
||||
* 文件的extra
|
||||
*/
|
||||
@ApiModelProperty(value = "extra")
|
||||
private String extra;
|
||||
|
||||
/**
|
||||
* 缩略图thumb
|
||||
*/
|
||||
// @NotBlank(message = "thumb不能为空")
|
||||
@ApiModelProperty(value = "缩略图thumb")
|
||||
private String thumb;
|
||||
|
||||
/**
|
||||
* dir (str): 存放图片的目录。下载图片需要。暂不支持视频
|
||||
*/
|
||||
@ApiModelProperty(value = "图片存放路径dir")
|
||||
private String dir;
|
||||
|
||||
}
|
@ -126,8 +126,12 @@ public class WeChatSocketClient {
|
||||
|
||||
public Response sendCmd(Request req) {
|
||||
try {
|
||||
// 设置发送 20 秒超时
|
||||
cmdSocket.setSendTimeout(20000);
|
||||
// 不知道之前设置20S有啥特殊情况???这里设置超时时间 5s --> 参考Python版本
|
||||
// 防止无响应的时候线程一直阻塞--ReceiveTimeout
|
||||
// modify by wmz 2025-05-03
|
||||
cmdSocket.setSendTimeout(5000);
|
||||
cmdSocket.setReceiveTimeout(5000);
|
||||
|
||||
ByteBuffer bb = ByteBuffer.wrap(req.toByteArray());
|
||||
cmdSocket.send(bb);
|
||||
ByteBuffer ret = ByteBuffer.allocate(BUFFER_SIZE);
|
||||
@ -655,7 +659,9 @@ public class WeChatSocketClient {
|
||||
* @param src 加密的图片路径
|
||||
* @param dir 保存图片的目录
|
||||
* @return 解密图片的保存路径
|
||||
* @see WeChatDllServiceImpl decryptImage
|
||||
*/
|
||||
@Deprecated
|
||||
public String decryptImage(String src, String dir) {
|
||||
Wcf.DecPath build = Wcf.DecPath.newBuilder().setSrc(src).setDst(dir).build();
|
||||
Request req = Request.newBuilder().setFuncValue(Functions.FUNC_DECRYPT_IMAGE_VALUE).setDec(build).build();
|
||||
|
@ -6,6 +6,7 @@ import com.wechat.ferry.entity.vo.request.WxPpWcfAddFriendGroupMemberReq;
|
||||
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseSqlReq;
|
||||
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseTableReq;
|
||||
import com.wechat.ferry.entity.vo.request.WxPpWcfDeleteGroupMemberReq;
|
||||
import com.wechat.ferry.entity.vo.request.WxPpWcfDownloadAttachReq;
|
||||
import com.wechat.ferry.entity.vo.request.WxPpWcfGroupMemberReq;
|
||||
import com.wechat.ferry.entity.vo.request.WxPpWcfInviteGroupMemberReq;
|
||||
import com.wechat.ferry.entity.vo.request.WxPpWcfPassFriendApplyReq;
|
||||
@ -487,4 +488,39 @@ public interface WeChatDllService {
|
||||
*/
|
||||
String receiveTransfer(String weChatUid, String transferId, String transactionId);
|
||||
|
||||
/**
|
||||
* 下载视频文件
|
||||
*
|
||||
* @param request 请求入参
|
||||
* @return 文件路径
|
||||
*
|
||||
* @author wmz
|
||||
* @throws java.lang.Exception
|
||||
* @date 2025-05-02
|
||||
*/
|
||||
String downloadVideo(WxPpWcfDownloadAttachReq request) throws Exception;
|
||||
|
||||
/**
|
||||
* 下载图片
|
||||
*
|
||||
* @param request 请求入参
|
||||
* @return 文件路径
|
||||
*
|
||||
* @author wmz
|
||||
* @throws java.lang.Exception
|
||||
* @date 2025-05-02
|
||||
*/
|
||||
String downloadPicture(WxPpWcfDownloadAttachReq request) throws Exception;
|
||||
|
||||
/**
|
||||
* 获取登录二维码
|
||||
*
|
||||
* @return 文件路径
|
||||
*
|
||||
* @author wmz
|
||||
* @throws java.lang.Exception
|
||||
* @date 2025-05-02
|
||||
*/
|
||||
String loginQR() throws Exception;
|
||||
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import com.wechat.ferry.entity.vo.request.WxPpWcfAddFriendGroupMemberReq;
|
||||
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseSqlReq;
|
||||
import com.wechat.ferry.entity.vo.request.WxPpWcfDatabaseTableReq;
|
||||
import com.wechat.ferry.entity.vo.request.WxPpWcfDeleteGroupMemberReq;
|
||||
import com.wechat.ferry.entity.vo.request.WxPpWcfDownloadAttachReq;
|
||||
import com.wechat.ferry.entity.vo.request.WxPpWcfGroupMemberReq;
|
||||
import com.wechat.ferry.entity.vo.request.WxPpWcfInviteGroupMemberReq;
|
||||
import com.wechat.ferry.entity.vo.request.WxPpWcfPassFriendApplyReq;
|
||||
@ -56,6 +57,8 @@ import com.wechat.ferry.exception.BizException;
|
||||
import com.wechat.ferry.handle.WeChatSocketClient;
|
||||
import com.wechat.ferry.service.WeChatDllService;
|
||||
import com.wechat.ferry.utils.HttpClientUtil;
|
||||
import com.wechat.ferry.utils.PathUtils;
|
||||
import java.io.File;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@ -762,5 +765,131 @@ public class WeChatDllServiceImpl implements WeChatDllService {
|
||||
throw new BizException("微信客户端未登录或状态异常,请人工关闭本服务之后,退出微信客户端在重启本服务!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String loginQR() throws Exception {
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
// 公共校验
|
||||
checkClientStatus();
|
||||
log.info("[登录]-[获取二维码]-入参打印:{}", "");
|
||||
// # 强制等待 1 秒让数据入库,避免那帮人总是嗷嗷叫超时
|
||||
Thread.sleep(1000);
|
||||
//第一步
|
||||
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_REFRESH_QRCODE_VALUE)
|
||||
.build();
|
||||
Wcf.Response rsp = wechatSocketClient.sendCmd(req);
|
||||
rsp.getStatus();
|
||||
// RequestResponseBodyMethodProcessor s;
|
||||
long endTime = System.currentTimeMillis();
|
||||
log.info("[登录]-[获取二维码]-处理结束,耗时:{}ms", (endTime - startTime));
|
||||
System.out.println(rsp.getStr());
|
||||
return rsp.getStr();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String downloadVideo(WxPpWcfDownloadAttachReq request) throws Exception {
|
||||
long startTime = System.currentTimeMillis();
|
||||
// 公共校验
|
||||
checkClientStatus();
|
||||
log.info("[下载]-[下载视频]-入参打印:{}", request);
|
||||
int status = attachDownload(request.getId(), "", request.getThumb());
|
||||
if (status != 0) {
|
||||
log.info("{}:下载视频出错", request.getId());
|
||||
return null;
|
||||
}
|
||||
//第二步,检测文件,指定下载的目录
|
||||
String base = PathUtils.removeExtension(request.getThumb());
|
||||
String filePath = base + ".mp4";
|
||||
String path = null;
|
||||
for (int i = 0; i < 30; i++) {
|
||||
if (new File(filePath).exists()) {
|
||||
path = filePath;
|
||||
log.info("视频下载完毕:{}", path);
|
||||
break;
|
||||
} else {
|
||||
log.info("等待下载中:{}", i);
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
}
|
||||
long endTime = System.currentTimeMillis();
|
||||
log.info("[下载]-[下载视频]-处理结束,耗时:{}ms", (endTime - startTime));
|
||||
return path;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String downloadPicture(WxPpWcfDownloadAttachReq request) throws Exception {
|
||||
long startTime = System.currentTimeMillis();
|
||||
// 公共校验
|
||||
checkClientStatus();
|
||||
log.info("[下载]-[下载图片]-入参打印:{}", request);
|
||||
int status = attachDownload(request.getId(), request.getExtra(), "");
|
||||
if (status != 0) {
|
||||
log.info("{}:下载出错", request.getId());
|
||||
return null;
|
||||
}
|
||||
//第二步解密图片--下载图片
|
||||
String filePath = decryptImage(request.getExtra(), request.getDir());
|
||||
String path = null;
|
||||
for (int i = 0; i < 15; i++) {
|
||||
if (new File(filePath).exists()) {
|
||||
log.info("图片下载完毕:{}", filePath);
|
||||
path = filePath;
|
||||
break;
|
||||
} else {
|
||||
log.info("等待下载中:{}", i);
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
}
|
||||
long endTime = System.currentTimeMillis();
|
||||
log.info("[下载]-[下载图片]-处理结束,耗时:{}ms", (endTime - startTime));
|
||||
return path;
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载附件(图片、视频、文件)。这方法别直接调用。
|
||||
*
|
||||
* @param id
|
||||
* @param extra
|
||||
* @param thumb
|
||||
* @return int: 0 为成功, 其他失败。
|
||||
* @throws Exception
|
||||
*/
|
||||
private int attachDownload(long id, String extra, String thumb) {
|
||||
try {
|
||||
//# 强制等待 1 秒让数据入库,避免那帮人总是嗷嗷叫超时
|
||||
Thread.sleep(1000);
|
||||
//第一步,下载
|
||||
Wcf.AttachMsg msg = Wcf.AttachMsg.newBuilder().setId(id)
|
||||
.setExtra(extra).setThumb(thumb)
|
||||
.build();
|
||||
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_DOWNLOAD_ATTACH_VALUE)
|
||||
.setAtt(msg)
|
||||
.build();
|
||||
Wcf.Response rsp = wechatSocketClient.sendCmd(req);
|
||||
return rsp.getStatus();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解密图片
|
||||
*
|
||||
* @param srcPath 加密的图片路径
|
||||
* @param dir 保存图片的目录
|
||||
* @return 是否成功
|
||||
*/
|
||||
public String decryptImage(String srcPath, String dir) {
|
||||
Wcf.DecPath build = Wcf.DecPath.newBuilder().setSrc(srcPath).setDst(dir).build();
|
||||
Wcf.Request req = Wcf.Request.newBuilder().setFuncValue(Wcf.Functions.FUNC_DECRYPT_IMAGE_VALUE).setDec(build).build();
|
||||
Wcf.Response rsp = wechatSocketClient.sendCmd(req);
|
||||
if (rsp != null && rsp.getStatus() == 0) {
|
||||
return rsp.getStr();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
68
clients/java/wcf-bmc/src/main/java/com/wechat/ferry/utils/PathUtils.java
vendored
Normal file
68
clients/java/wcf-bmc/src/main/java/com/wechat/ferry/utils/PathUtils.java
vendored
Normal file
@ -0,0 +1,68 @@
|
||||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package com.wechat.ferry.utils;
|
||||
|
||||
/**
|
||||
* @date 2025-05-02
|
||||
* @author zm
|
||||
*/
|
||||
import java.io.File;
|
||||
|
||||
public class PathUtils {
|
||||
|
||||
/**
|
||||
* 分离文件路径,获取不带扩展名的部分 等同于 Python 的 os.path.splitext(thumb)[0]
|
||||
*/
|
||||
public static String removeExtension(String path) {
|
||||
int dotIndex = path.lastIndexOf('.');
|
||||
if (dotIndex == -1) {
|
||||
return path;
|
||||
}
|
||||
return path.substring(0, dotIndex);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件名(包含扩展名) 等同于 Python 的 os.path.basename(file_path)
|
||||
*/
|
||||
public static String getFileName(String path) {
|
||||
File file = new File(path);
|
||||
return file.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 不存在则创建
|
||||
* @param dirPath
|
||||
* @return
|
||||
*/
|
||||
public static boolean createDir(String dirPath) {
|
||||
File dir = new File(dirPath);
|
||||
if (!dir.exists()) {
|
||||
boolean created = dir.mkdirs(); // 创建目录,包括任何不存在的父目录
|
||||
if (created) {
|
||||
System.out.println("目录创建成功: " + dirPath);
|
||||
} else {
|
||||
System.out.println("目录创建失败: " + dirPath);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
//Nothing to do
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 示例用法
|
||||
public static void main(String[] args) {
|
||||
String thumb = "/tmp/video_cover.jpg";
|
||||
|
||||
String base = removeExtension(thumb); // /tmp/video_cover
|
||||
String filePath = base + ".mp4"; // /tmp/video_cover.mp4
|
||||
String fileName = getFileName(filePath); // video_cover.mp4
|
||||
|
||||
System.out.println("base: " + base);
|
||||
System.out.println("filePath: " + filePath);
|
||||
System.out.println("fileName: " + fileName);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user