Springboot优雅实现大文件上传

1. 前言

在文件相对较小的情况下,可以直接吧文件转化为字节流上传到服务器,但是在文件较大的情况下,用普通的方式进行上传,这可不是一个好办法,毕竟很少有人能忍受文件上传到一半终端后,继续上传却只能冲头开始,让人很不爽。

2. 详细教程

2.1 秒传

2.1.1 什么是秒传

通俗的说,你把要上传的东西上传,服务器会先做MD5校验,如果服务器上有一样的东西,他就直接给你个新地址,其实你下载的都是服务器上的同一个文件,想要不秒传,其实只要让MD5改变,就是对文件本身做一下修改(改名字不行),如果一个文本文件,多加几个字,MD5就变了,就不会秒传了

2.1.2 秒传的核心逻辑

  1. 利用redis的set方法存放文件上传状态,其中key为文件上传的md5,value为是否上传完成的标志位

  2. 当标志位true表示上传完成,此时如果有相同文件上传,则进入秒传逻辑。如果标志位为false,表示没有上传完成,此时需要再调用set方法,保存块号文件记录的路径,其中key为上传文件md5加一个固定前缀,value为块号文件记录路径

2.2 分片上传

2.2.1 什么是分片上传

分片上传就是将所要上传的文件,按照一定的大小,将整个文件分割成多个数据块(part)来进行分别上传,上传之后再有服务器对所有上传的文件进行汇总整合称为原始文件。

2.2.2 分片上传的场景

  1. 大文件上传

  2. 网络环境不好,存在需要重传风险的场景

2.3 断点续传

什么是断点续传

断点续传是在下载货上传是,将下载或上传任务(一个文件货一个压缩包)人为的划分为几个部分,每一个部分采用一个线程进行上传货下载,如果碰到网络故障,可以从已经上传或下载的部分开始继续上传或下载未完成的部分,而没有必要从头开始

2.3.1 应用场景

断点续传可以看成是分片上传的一个衍生,因此可以使用分片上传的场景,都可以使用断点续传

2.3.2 断点续传核心逻辑

在分片上传的过程中,如果因为系统崩溃或者网络中断等异常因素导致上传中断,这时候客户端需要记录上传的进度。在之后支持再次上传时,可以继续从上次上传中断的地方进行继续上传。

为了避免客户端在上传之后的进度数据被删除而导致重新开始从头上传的问题,服务端也可以提供相应的接口便于客户端对已经上传的分片数据进行查询,从而是客户端知道已经上传的分片数据,从而从下一个分片数据开始继续上传。

2.3.3 实现流程

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

    • 前端(客户端)需要根据固定大小对文件进行分片,请求后端(服务器)时要带上分片序号和大小

    • 服务端创建conf文件来记录分块位置,conf文件长度为总片数,没上传一个分块即向conf文件写入一个127,那么没有上传的位置就默认0。已上传的就是Byte.MAX_VALUE 127,这部时实现断点续传和秒传的核心逻辑

    • 服务器按照请求数据中的分片序号和每片分块大小(分片大小是固定且一样的)酸楚开始位置,与读取到的文件片段数据,写入文件

2.3.4 分片上传/断点上传代码实现

  1. 前端采用百度提供的webuploader插件进行分片,具体实现可以查看:
    request
    1
    http://fex.baidu.com/webuploader/getting-started.html
  2. 后端用两种方式实现写入,一种是RandomAccessFile,如果对RandomAccessFile不熟悉的可以查看:
    request
    1
    https://blog.csdn.net/dimudan2015/article/details/81910690
    另一种是使用MappedByteBuffer,具体详情可以查看链接:
    request
    1
    https://www.jianshu.com/p/f90866dcbffc