前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >微服务架构 | 怎样解决超大附件分片上传?

微服务架构 | 怎样解决超大附件分片上传?

作者头像
码农架构
修改2021-12-26 13:00:52
8590
修改2021-12-26 13:00:52
举报
文章被收录于专栏:码农架构码农架构

导读:分片上传、断点续传,这两个名词对于做过或者熟悉文件上传的朋友来说应该不会陌生,总结本篇文章希望对从事相关工作的同学能够有所帮助或者启发。

当我们的文件特别大的时候,上传是不是需要很长的时间啊,这么长时间的长连接,如果网络波动了呢?中间网络断开了呢?在这么长时间的过程中如果出现不稳定的情况,本次上传的所有内容就全部失败了,又要重新上传。

分片上传,就是将所要上传的文件,按照一定的大小,将整个文件分隔成多个数据块(我们称之为Part)来进行分别上传,上传完之后再由服务端对所有上传的文件进行汇总整合成原始的文件。分片上传不仅可以避免因网络环境不好导致的一直需要从文件起始位置还是上传的问题,还能使用多线程对不同分块数据进行并发发送,提高发送效率,降低发送时间。

一、背景

在系统用户量突增以后,为了更好适配各群体的定制化需求。业务上慢慢实现了支持C端用户自定义布局和配置,导致配置数据读取IO激增。

为了更好优化此类场景,将用户自定义配置静态化管理!也就是将对应的配置文件生成静态文件,在生成静态文件的过程中遇到棘手的问题,配置文件文件过大导致在文件上传服务器等待时间过长,致使整个业务场景性能整体下滑。

二、生成配置文件

生成文件三大要素

  • 文件名
  • 文件内容
  • 文件存储格式

文件内容、文件存储格式都好理解和处理,当然先前整理过微服务中常用的加密方式

这里做下补充说明,如果要想对文件内容进行加密可以考虑。但是本文的案例场景对于配置信息保密程度较低,这里不做拓展。

而对于文件名的命名规范具体结合业务场景来定,通常都是以文件概要+时间戳格式为主。但是这类命名规范容易导致文件名冲突,造成没有必要的后续麻烦。

所以我这里对于文件名的命名做了特殊处理,有处理过前端Route路由经验的应该能联想到,文件名可以通过基于内容生成Hash值来代替。

Spring 3.0 之后提供了计算摘要的的方法。

代码语言:javascript
复制
DigestUtils#md

返回给定字节的 MD5 摘要的十六进制字符串表示形式。

md5DigestAsHex源码

代码语言:javascript
复制
/**
 * 计算摘要的字节
 * @param  一个十六进制摘要字符
 * @return 串返回给定字节的 MD5 摘要的十六进制字符串表示形式。
 */
public static String md5DigestAsHex(byte[] bytes) {
  return digestAsHexString(MD5_ALGORITHM_NAME, bytes);
}

文件名、内容、后缀(存储格式)确定后直接生成文件

代码语言:javascript
复制
/**
  * 直接根据内容生成 文件
  */
public static void generateFile(String destDirPath, String fileName, String content) throws FileZipException {
    File targetFile = new File(destDirPath + File.separator + fileName);
      //确保父级目录存在
      if (!targetFile.getParentFile().exists()) {
        if (!targetFile.getParentFile().mkdirs()) {
          throw new FileZipException(" path is not found ");
        }
      }
    //设置文件编码格式
    try (PrintWriter writer = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(targetFile), ENCODING)))
        ) {
      writer.write(content);
      return;
    } catch (Exception e) {
      throw new FileZipException("create file error",e);
    }
}

通过内容生成文件优点不言而喻,可以极大减少我们主动基于内容比较来生成新的文件、如果文件内容较大生成对应的文件名相同则表示内容未做任何调整,此时我们也就不用做后续的文件更新操作。

三、分片上传附件

所谓的分片上传,就是将所要上传的文件,按照一定的大小,将整个文件分隔成多个数据块(我们称之为Part)来进行分别上传,上传完之后再由服务端对所有上传的文件进行汇总整合成原始的文件。分片上传不仅可以避免因网络环境不好导致的一直需要从文件起始位置还是上传的问题,还能使用多线程对不同分块数据进行并发发送,提高发送效率,降低发送时间。

分片上传主要适用于以下几种场景:

  • 网络环境不好:当出现上传失败的时候,可以对失败的Part进行独立的重试,而不需要重新上传其他的Part。
  • 断点续传:中途暂停之后,可以从上次上传完成的Part的位置继续上传。
  • 加速上传:要上传到OSS的本地文件很大的时候,可以并行上传多个Part以加快上传。
  • 流式上传:可以在需要上传的文件大小还不确定的情况下开始上传。这种场景在视频监控等行业应用中比较常见。
  • 文件较大:一般文件比较大时,默认情况下一般都会采用分片上传。

分片上传的整个流程大致如下:

  • 将需要上传的文件按照一定的分割规则,分割成相同大小的数据块;
  • 初始化一个分片上传任务,返回本次分片上传唯一标识;
  • 按照一定的策略(串行或并行)发送各个分片数据块;
  • 发送完成后,服务端根据判断数据上传是否完整,如果完整,则进行数据块合成得到原始文件

▐ 定义分片规则大小

默认情况都以文件达到20MB进行强制分片

代码语言:javascript
复制
/**
 * 强制分片文件大小(20MB)
 */
long FORCE_SLICE_FILE_SIZE = 20L* 1024 * 1024;

为了方便调试,强制分片文件的阈值调整为1KB

定义分片上传对象

如上图红色序号的文件碎片,定义分片上传对象基础属性包含附件文件名、原始文件大小、原始文件MD5值、分片总数、每个分片大小、当前分片大小、当前分片序号等

定义基础属于便于后续对文件合理分割、分片的合并等业务拓展,当然根据业务场景可以定义拓展属性。

  • 分片总数
代码语言:javascript
复制
long totalSlices = fileSize % forceSliceSize == 0 ? 
    fileSize / forceSliceSize : fileSize / forceSliceSize + 1;
  • 每个分片大小
代码语言:javascript
复制
long eachSize = fileSize % totalSlices == 0 ? 
    fileSize / totalSlices : fileSize / totalSlices + 1;
  • 原始文件的MD5值
代码语言:javascript
复制
MD5Util.hex(file)

如:

当前附件大小为:3382KB,强制分片大小限制为1024KB

通过上述计算:分片数量为4个每个分片大小为846KB

▐ 读取每个分片的数据字节

标记当前字节下标,循环读取4个分片的数据字节

代码语言:javascript
复制
try (InputStream inputStream = new FileInputStream(uploadVO.getFile())) {
    for (int i = 0; i < sliceBytesVO.getFdTotalSlices(); i++) {
        // 读取每个分片的数据字节
        this.readSliceBytes(i, inputStream, sliceBytesVO);
      // 调用分片上传API的函数
        String result = sliceApiCallFunction.apply(sliceBytesVO);
        if (StringUtils.isEmpty(result)) {
            continue;
        }
        return result;
    }
} catch (IOException e) {
    throw e;
}

三、总结

所谓的分片上传,就是将所要上传的文件,按照一定的大小,将整个文件分隔成多个数据块(我们称之为Part)来进行分别上传。

处理大文件进行分片主要核心确定三大点

  • 文件分片粒度大小
  • 分片如何读取
  • 分片如何存储

本篇文章主要分析和处理大文件上传过程中如何针对大文件文件文件内容比较、进行分片处理。合理设置分片阈值以及如何读取和标记分片。希望对从事相关工作的同学能够有所帮助或者启发。后续会对分片如何存储、标记、合并文件进行详细解读。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2021-12-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农架构 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 二、生成配置文件
  • 三、分片上传附件
    • ▐ 定义分片规则大小
      • ▐ 定义分片上传对象
        • ▐ 读取每个分片的数据字节
        • 三、总结
        相关产品与服务
        对象存储
        对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档