企业微信会话存档-一个文件下载,吹牛2小时完成,结果写了2星期?

需求:

获取聊天记录里产生的文件,下载并保存,存储到文件服务器(用oss表示)中

过程:

看起来很简单,直接干;

初步思路:

  1. 请求企业微信文件下载接口,先下载到本地
  2. 从本地读取文件,上传到oss

看起来并没什么问题,直接干!

踩坑1

既然是从企业微信接口下载的,那就先看看企业微信的开发文档吧

notes-markdown-km.oss-cn-beijing.aliyuncs.com

纳尼,这些参数是什么???

好吧,获取这些参数的方式请参考《企业微信会话存档-如何高性能存储海量聊天记录》,这里就不深究了:获取聊天记录,消息类型有很多,如果消息包含文件类型,那就会包含以上字段;

因为涉及其他业务,一条消息可能需要多次处理,所以处理消息的方式使用事件广播的方式,这里只需要监听消息事件就可以了。

通过API文档了解到,媒体消息是需要进行分片拉取的,每个分片大小最大为512kb;

实际的代码太长了,为方便理解使用伪代码表示,如下(文中所有的代码均为伪代码)

// 0. 记录文件信息表
inert(fileInfo);
// 1. 初始化sdk;验证秘钥并解密
sdk = init();
// 2. 创建文件
file = createFile();
byte[] b = new byte[0];

// 3. 拉取消息
indexBuf = "";
while(true) {
    // 3.1 获取数据
    result = getMediaData(indexBuf);
    // 3.2 将数据保存
    // 企微返回的就是byte数组
    byte[] b1 = result.data;
    b = b.length == 0 ? b1 : ArrayUtil.addAll(b, b1);
    // 3.3 判断文件是否下载完成;1表示下载完成
    if (1 == isFinish) {
        break;
    } else {
        // 记录 indexBuf,即文件下载到哪里了,下次请求带上该参数
        indexBuf = result.indexBuf;
    }
}
// 4. 保存到文件
file.write(b);
// 5. 上传到oss
ossService.upload(file);
// 6. 删除文件
del(file);

写到这里,完成了,上线上线

踩坑2,3,4,5,6…

一个一个写太慢了,篇幅长而无用也是浪费读者时间,这里一并列举:

  1. 项目跑的时候,同事发了个win11的系统镜像;哎,系统怎么停了,服务器内存怎么满了?
  2. 一个一个下载文件,太慢了,改成多线程吧;使用线程池,最大开了8个线程,等待队列开了20个;项目重启,刚才的文件呢,怎么丢了一部分?
  3. oss的服务怎么突然停了,那我的项目怎么跑?
  4. 纳尼,不用oss了,换成s3服务器了?因为oss在升级,过段时间再换回来?内心…
  5. 怎么客户发送了个文件,消息记录有这条消息,但是文件没法下载呢,也没个提示?
  6. 大文件就算了,我就发了个不到1M的文件,就算有延迟,也不应该等了几个小时还没下载完吧?
  7. 为什么有的文件没有传到oss呢,没传到oss的会丢失吗?
  8. 文件太多了,能不能优化一下,节省一些带宽?
  9. 客户:为什么,为什么,为什么…能不能这样处理…想要个这样的效果…
修复ing

剖析原始代码的问题:

  1. 使用byte[] 数组接收数据,但是所有的数据都是存储在内存中的,文件过大会把内存占满
  2. 因为是请求的企业微信,外网通信,难免会因为网络等原因导致请求失败,如果中途请求失败,文件会丢失
  3. 文件下载完成之后,直接上传oss,若上传失败,文件又会丢失

逐个针对单个问题简单说一下解决思路(单个看起来有点儿乱,可直接看完整解决方案):

  1. byte[] 更换为 FileOutputStream;每次while循环里都直接写入并flush();while完成后关闭流;可解决大文件读取在内存中,导致内存不足的问题
  2. 等待队列改为0;文件记录存入redis;重启项目不会丢失数据
  3. 记录文件id和状态,使用定时任务去重试上传oss
  4. 抽象一个文件上传第三方服务器的接口,把使用第三方服务的类型放进配置中心,修改配置中心即可更换上传方式
  5. 将文件下载的状态也记录起来,同步到页面展示
  6. 有可能刚好几个线程全是下载的大文件,都很耗时;将大文件小文件分开下载
  7. 重复多次上传,一直失败,就永久保存,提供文件访问接口
  8. 使用md5校验文件内容是否一致,如果相同就不重复下载和上传

完整解决方案

经过不断的踩坑,不断的修复,终于得到了一个较为完善的解决方案;

处理流程中,文件有多种状态,为保证文件不丢失,将文件状态记录到表中;各状态分别是:

/**
 * -1 初始化值;监听到消息就记录值
 */
Integer INIT = -1;
/**
 * 0-微信下载中
 */
Integer DOWNLOADING = 0;
/**
 * 1-微信下载失败
 */
Integer WECHAT_ERROR = 1;
/**
 * 2-文件存储失败
 */
Integer SAVE_ERROR = 2;

/**
 * 6-文件无需下载
 */
Integer NOT_NEED_DOWNLOAD = 6;
/**
 * 7-文件存储成功
 */
Integer SUCCESS = 7;
/**
 * 8-文件转存oss失败
 */
Integer OSS_ERROR = 8;
/**
 * 9-文件转存oss成功
 */
Integer OSS_SUCCESS = 9;

程序的执行流程,纯文字看起来太迷糊了,来张图看吧

文件存储

总结

总结就是上图

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值