文件分片上传和下载

最近遇到一个问题,一客户要求将我们的平台部署到新的环境中,但是这个环境的网关对网络传输内容的大小进行了限制,而我们的平台又有很多传输文件的功能,传输内容大小大概率会超过网关限制,因此,我这边就开始想解决方案,最终经过综合考虑,分片上传和下载最为合适(可能会有疑问为什么不用oss,这个是因为客户内部的oss存储,对文件大小依然有限制,和网关限制相同)。

这次虽然是为了客户环境而改用的分片上传和下载,但是,该功能在实际应用中有很多好处:

  • 提高传输效率:用户可以从多个服务器或多个镜像站点同时下载文件的不同部分,这样可以显著提高下载速度。
  • 减少服务器压力:下载请求被分散到多个分片中,每个分片可以独立处理,从而减轻服务器的负载。
  • 断点续传:如果下载过程中发生中断,用户可以从断点继续下载剩余的部分,无需从头开始。
  • 支持大文件下载:对于大型文件,分片下载允许即使在没有足够带宽的情况下也能下载。
  • 并行处理:下载多个分片可以并行处理,这有助于缩短总的下载时间。
  • 可靠性:如果下载过程中某个分片损坏或丢失,可以只重新下载那个分片,而不是整个文件。
  • 安全性:分片下载可以减少单次下载的数据量,从而降低数据泄露的风险。

实现

分片上传

其实原理很简单,步骤如下:

  1. 分片,主要是把文件进行拆分,分成若干个小文件
  2. 前端调用接口分别进行上传
  3. 前端上传完后,调用另一个接口,通知服务端,切片上传完成
  4. 服务端收到前端的通知后,进行文件合并

对于后端,要提供两个接口。(通过md5值将分片的文件存储到指定位置)

  1. 接收分片文件的接口,接口(post)参数如下:
参数名 说明
chunked_file file,分片文件
file_md5 string,原始文件的md5值,用来唯一表示文件,后续合并请求,通过该值,找到切片文件,然后进行合并
chunked_index string, 原始文件会切成多个文件,文件的先后顺序,0,1,2...方便服务端根据顺序进行合并文件
  1. 分片上传完成通知接口,接口(post)参数如下:
参数名 说明
file_md5 string,原始文件的md5值
filename string, 文件名,合并时,会以该名进行命名

[!WARNING] 对于前端来说,分片时一定要分对,注意把控好每次分片的范围,千万别丢数据,在实际开发中,我们就遇到了这个问题

分片下载

分片下载,主要用到了HTTP范围请求,按照该规范进行接口调用,我用django实现的代码大概如下(细节方面还有欠缺):

    file_path = request.data.get('file_path')
    range_header = request.headers.get('Range', '')
    pattern = r'bytes=(\d+)-(\d+)'
    matches = re.match(pattern, range_header)
    if matches:
        start_pos = int(matches.group(1))
        end_pos = int(matches.group(2))
    else:
        return Response(dict(msg="Range Not Satisfiable"), status=416)
    fb = open(file_path, 'rb')
    fb.seek(start_pos)
    response = FileResponse(fb, as_attachment=True, status=206)
    total_size = os.path.getsize(file_path)
     # Accept-Ranges
    response['Accept-Ranges'] = "bytes"
    real_end_pos = total_size - 1 if end_pos > total_size else end_pos
    response['Content-Range'] = f"bytes {start_pos}-{real_end_pos}/{total_size}"
    response['Content-Length'] = real_end_pos - start_pos + 1 # 该值决定了下载的最终大小
    return response

reference

results matching ""

    No results matching ""