Quellcode durchsuchen

:art: #2858 【企业微信】优化会话存档获取媒体数据的接口

Alixhan vor 2 Jahren
Ursprung
Commit
30264bfaae

+ 17 - 0
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpMsgAuditService.java

@@ -5,6 +5,7 @@ import me.chanjar.weixin.common.error.WxErrorException;
 import me.chanjar.weixin.cp.bean.msgaudit.*;
 
 import java.util.List;
+import java.util.function.Consumer;
 
 /**
  * 会话内容存档接口.
@@ -73,6 +74,22 @@ public interface WxCpMsgAuditService {
                     @NonNull String targetFilePath) throws WxErrorException;
 
   /**
+   * 获取媒体文件 传入一个lambda,each所有的数据分片byte[],更加灵活
+   * 针对图片、文件等媒体数据,提供sdk接口拉取数据内容。
+   * 详情可以看官方文档,亦可阅读此接口源码。
+   *
+   * @param sdk            getChatDatas()获取到的sdk,注意,每次获取的sdk会不一样
+   * @param sdkfileid      消息体内容中的sdkfileid信息
+   * @param proxy          使用代理的请求,需要传入代理的链接。如:socks5://10.0.0.1:8081 或者 http://10.0.0.1:8081,如果没有传null
+   * @param passwd         代理账号密码,需要传入代理的账号密码。如 user_name:passwd_123,如果没有传null
+   * @param timeout        超时时间,分片数据需累加到文件存储。单次最大返回512K字节,如果文件比较大,自行设置长一点,比如timeout=10000
+   * @param action         传入一个lambda,each所有的数据分片
+   * @throws WxErrorException the wx error exception
+   */
+  void getMediaFile(@NonNull long sdk, @NonNull String sdkfileid, String proxy, String passwd, @NonNull long timeout,
+                    @NonNull Consumer<byte[]> action) throws WxErrorException;
+
+  /**
    * 获取会话内容存档开启成员列表
    * 企业可通过此接口,获取企业开启会话内容存档的成员列表
    * <p>

+ 23 - 5
weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/impl/WxCpMsgAuditServiceImpl.java

@@ -21,6 +21,7 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.function.Consumer;
 
 import static me.chanjar.weixin.cp.constant.WxCpApiPathConsts.MsgAudit.*;
 
@@ -80,10 +81,10 @@ public class WxCpMsgAuditServiceImpl implements WxCpMsgAuditService {
     long sdk = Finance.NewSdk();
     //因为会话存档单独有个secret,优先使用会话存档的secret
     String msgAuditSecret = cpService.getWxCpConfigStorage().getMsgAuditSecret();
-    if(StringUtils.isEmpty(msgAuditSecret)) {
+    if (StringUtils.isEmpty(msgAuditSecret)) {
       msgAuditSecret = cpService.getWxCpConfigStorage().getCorpSecret();
     }
-    long ret = Finance.Init(sdk, cpService.getWxCpConfigStorage().getCorpId(),msgAuditSecret);
+    long ret = Finance.Init(sdk, cpService.getWxCpConfigStorage().getCorpId(), msgAuditSecret);
     if (ret != 0) {
       Finance.DestroySdk(sdk);
       throw new WxErrorException("init sdk err ret " + ret);
@@ -181,7 +182,26 @@ public class WxCpMsgAuditServiceImpl implements WxCpMsgAuditService {
     if (!targetFile.getParentFile().exists()) {
       targetFile.getParentFile().mkdirs();
     }
+    this.getMediaFile(sdk, sdkfileid, proxy, passwd, timeout, i -> {
+      try {
+        // 大于512k的文件会分片拉取,此处需要使用追加写,避免后面的分片覆盖之前的数据。
+        FileOutputStream outputStream = new FileOutputStream(targetFile, true);
+        outputStream.write(i);
+        outputStream.close();
+      } catch (Exception e) {
+        e.printStackTrace();
+      }
+    });
+  }
 
+  @Override
+  public void getMediaFile(@NonNull long sdk, @NonNull String sdkfileid, String proxy, String passwd, @NonNull long timeout, @NonNull Consumer<byte[]> action) throws WxErrorException {
+/**
+ * 1、媒体文件每次拉取的最大size为512k,因此超过512k的文件需要分片拉取。
+ * 2、若该文件未拉取完整,sdk的IsMediaDataFinish接口会返回0,同时通过GetOutIndexBuf接口返回下次拉取需要传入GetMediaData的indexbuf。
+ * 3、indexbuf一般格式如右侧所示,”Range:bytes=524288-1048575“:表示这次拉取的是从524288到1048575的分片。单个文件首次拉取填写的indexbuf
+ * 为空字符串,拉取后续分片时直接填入上次返回的indexbuf即可。
+ */
     String indexbuf = "";
     int ret, data_len = 0;
     log.debug("正在分片拉取媒体文件 sdkFileId为{}", sdkfileid);
@@ -200,9 +220,7 @@ public class WxCpMsgAuditServiceImpl implements WxCpMsgAuditService {
 
       try {
         // 大于512k的文件会分片拉取,此处需要使用追加写,避免后面的分片覆盖之前的数据。
-        FileOutputStream outputStream = new FileOutputStream(new File(targetFilePath), true);
-        outputStream.write(Finance.GetData(mediaData));
-        outputStream.close();
+        action.accept(Finance.GetData(mediaData));
       } catch (Exception e) {
         e.printStackTrace();
       }

+ 117 - 10
weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpMsgAuditTest.java

@@ -12,8 +12,11 @@ import me.chanjar.weixin.cp.constant.WxCpConsts;
 import me.chanjar.weixin.cp.demo.WxCpDemoInMemoryConfigStorage;
 import me.chanjar.weixin.cp.util.xml.XStreamTransformer;
 import org.eclipse.jetty.util.ajax.JSON;
+import org.testng.annotations.BeforeTest;
 import org.testng.annotations.Test;
 
+import java.io.File;
+import java.io.FileOutputStream;
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -39,17 +42,21 @@ public class WxCpMsgAuditTest {
    */
 // com.binarywang.spring.starter.wxjava.cp.config.WxCpServiceAutoConfiguration
   // WxCpServiceImpl.getAccessToken()
-  @Test
-  public void test() throws Exception {
-
-    InputStream inputStream = ClassLoader.getSystemResourceAsStream("test-config.xml");
-    WxCpDemoInMemoryConfigStorage config = WxCpDemoInMemoryConfigStorage.fromXml(inputStream);
-
-    wxCpConfigStorage = config;
-    cpService = new WxCpServiceImpl();
-    cpService.setWxCpConfigStorage(config);
 
+  @BeforeTest
+  private void initCpservice() {
+    if(cpService == null) {
+      InputStream inputStream = ClassLoader.getSystemResourceAsStream("test-config.xml");
+      WxCpDemoInMemoryConfigStorage config = WxCpDemoInMemoryConfigStorage.fromXml(inputStream);
+      config.setMsgAuditLibPath("/E:/IDEA_WORKSPACE/saisc/crs-member-java/target/classes/wework/libcrypto-1_1-x64.dll,libssl-1_1-x64.dll,libcurl-x64.dll,WeWorkFinanceSdk.dll");
+      wxCpConfigStorage = config;
+      cpService = new WxCpServiceImpl();
+      cpService.setWxCpConfigStorage(config);
+    }
+  }
 
+  @Test
+  public void test() throws Exception {
     /**
      * 客户同意进行聊天内容存档事件回调
      * 配置了客户联系功能的成员添加外部联系人同意进行聊天内容存档时,回调该事件。
@@ -644,9 +651,109 @@ public class WxCpMsgAuditTest {
      * https://www.jianshu.com/p/dde171887d63
      */
     String getUrl = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=%s&corpsecret=%s";
-    String data = cpService.get(String.format(getUrl, config.getCorpId(), config.getCorpSecret()), null);
+    String data = cpService.get(String.format(getUrl, wxCpConfigStorage.getCorpId(), wxCpConfigStorage.getCorpSecret()), null);
 
 
   }
 
+  @Test
+  public void testGetMediaFile() throws Exception {
+    WxCpMsgAuditService msgAuditService = cpService.getMsgAuditService();
+    WxCpChatDatas chatDatas = msgAuditService.getChatDatas(0L, 100L, null, null, 10);
+    for (WxCpChatDatas.WxCpChatData chatDatum : chatDatas.getChatData()) {
+      WxCpChatModel decryptData = msgAuditService.getDecryptData(chatDatas.getSdk(), chatDatum, 2);
+        // 文件后缀
+        String suffix = "";
+        // 文件名md5
+        String md5Sum = "";
+        // sdkFileId
+        String sdkFileId = "";
+      String msgType = decryptData.getMsgType();
+      if(msgType == null ) msgType = "";
+      switch (msgType) {
+          case "image":
+            suffix = ".jpg";
+            md5Sum = decryptData.getImage().getMd5Sum();
+            sdkFileId = decryptData.getImage().getSdkFileId();
+            break;
+          case "voice":
+            suffix = ".amr";
+            md5Sum = decryptData.getVoice().getMd5Sum();
+            sdkFileId = decryptData.getVoice().getSdkFileId();
+            break;
+          case "video":
+            suffix = ".mp4";
+            md5Sum = decryptData.getVideo().getMd5Sum();
+            sdkFileId = decryptData.getVideo().getSdkFileId();
+            break;
+          case "emotion":
+            md5Sum = decryptData.getEmotion().getMd5Sum();
+            sdkFileId = decryptData.getEmotion().getSdkFileId();
+            int type = decryptData.getEmotion().getType();
+            switch (type) {
+              case 1:
+                suffix = ".gif";
+                break;
+              case 2:
+                suffix = ".png";
+                break;
+              default:
+                return;
+            }
+            break;
+          case "file":
+            md5Sum = decryptData.getFile().getMd5Sum();
+            suffix = "." + decryptData.getFile().getFileExt();
+            sdkFileId = decryptData.getFile().getSdkFileId();
+            break;
+          // 音频存档消息
+          case "meeting_voice_call":
+
+            md5Sum = decryptData.getVoiceId();
+            sdkFileId = decryptData.getMeetingVoiceCall().getSdkFileId();
+            for (WxCpChatModel.MeetingVoiceCall.DemoFileData demofiledata :
+              decryptData.getMeetingVoiceCall().getDemoFileData()) {
+              String demoFileDataFileName = demofiledata.getFileName();
+              suffix = demoFileDataFileName.substring(demoFileDataFileName.lastIndexOf(".") + 1);
+            }
+
+            break;
+          // 音频共享文档消息
+          case "voip_doc_share":
+
+            md5Sum = decryptData.getVoipId();
+            WxCpFileItem docShare = decryptData.getVoipDocShare();
+            String fileName = docShare.getFileName();
+            suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
+            break;
+          default:
+            continue;
+        }
+
+        /**
+         * 拉取媒体文件
+         *
+         * 传入一个each函数,用于遍历每个分片的数据
+         */
+      String path = Thread.currentThread().getContextClassLoader().getResource("").getPath();
+      String targetPath = path + "testfile/" + md5Sum + suffix;
+      File file = new File(targetPath);
+      if (!file.getParentFile().exists()) {
+        file.getParentFile().mkdirs();
+      }else{
+        file.delete();
+      }
+        cpService.getMsgAuditService().getMediaFile(chatDatas.getSdk(), sdkFileId, null, null, 1000L, data -> {
+          try {
+            // 大于512k的文件会分片拉取,此处需要使用追加写,避免后面的分片覆盖之前的数据。
+            FileOutputStream outputStream = new FileOutputStream(targetPath, true);
+            outputStream.write(data);
+            outputStream.close();
+          } catch (Exception e) {
+            e.printStackTrace();
+          }
+        });
+    }
+    Finance.DestroySdk(chatDatas.getSdk());
+  }
 }

+ 1 - 0
weixin-java-cp/src/test/resources/test-config.sample.xml

@@ -12,6 +12,7 @@
   <oauth2redirectUri>网页授权获取用户信息回调地址</oauth2redirectUri>
   <webhookKey>webhook链接地址的key值</webhookKey>
   <!-- 企业微信会话存档,私钥,windows以及linux环境sdk路径 -->
+  <msgAuditSecret>会话存档的secret</msgAuditSecret>
   <msgAuditPriKey>-----BEGIN RSA PRIVATE KEY-----
     MIICXAIBAAKBgQCTfm5cxqfglfOV7b/Z7OtTZZoZpk2EPTvVhn/ngsfKR899xRdR
     25s4h8HkK0XhxqYdOGoAdG3Gms+DvCSY/vu3UtImf0eZSNXpKZJBUnvUVjX4ivnr