断点下载

背景

    断点续传/断点下载一直是每个系统最实用的功能,最近公司在复杂的网络环境(国外vps)下载东西遇到问题,有些文件下载的时候很慢,并且可能会下不下来,这种情况对一个系统的稳定性构成很大的威胁,所以必须要采用断点下载,并且需要自定义设置下载大小。
    然而网上都是关于客户端下载的方法,没有关于服务端的实现方法。根据网络环境的复杂及网速设计了一个可以自定义下载大小的服务端/客户端代码。

原理

利用请求头和响应头
Range是一个HTTP请求头,告知服务器要返回文件的哪一部分,即:哪个区间范围(字节)的数据,在 Range 中,可以一次性请求多个部分,服务器会以 multipart 文件的形式将其返回。如果服务器返回的是范围响应,需要使用 206 Partial Content 状态码。假如所请求的范围不合法,那么服务器会返回 416 Range Not Satisfiable 状态码,表示客户端错误。服务器允许忽略 Range 头,从而返回整个文件,状态码用 200 。
当你正在看大片时,网络断了,你需要继续看的时候,文件服务器不支持断点的话,则你需要重新等待下载这个大片,才能继续观看。而Range支持的话,客户端就会记录了之前已经看过的视频文件范围,网络恢复之后,则向服务器发送读取剩余Range的请求,服务端只需要发送客户端请求的那部分内容,而不用整个视频文件发送回客户端,以此节省网络带宽。
Range 格式
Range: bytes=0-32768

交互过程

客户端请求下载时会带上Range头。Range 表示下载的范围区间,0-1024就是下载开始的1024bytes, 1024-就是从1024开始到结束,1024-32768就是下载1024-32768之间的bytes。
服务端会根据HTTP_RANGE来获取客户端需要的文件大小和文件大小的范围,然后以Content-Range=0-1024/32768来返回,/之后代表的是整个文件的大小,之前代表的是当前的文件区间内容大小

废话不多说,上代码

代码实现

Server 端代码如下

import statfrom django.http import FileResponse, JsonResponseimport os
import re
import posixpath
import mimetypes
from django.utils._os import safe_join# Create your views here.def down_file_iterator(file_path, start_pos, chunk_size):"""文件生成器,防止文件过大,导致内存溢出:param file_path: 文件绝对路径:param start_pos: 文件读取的起始位置:param chunk_size: 文件读取的块大小:return: yield"""with open(file_path, mode='rb') as f:f.seek(start_pos, os.SEEK_SET)content = f.read(chunk_size)yield contentdef breakpoint_download(request, filename):"""断点下载:param request: request:param filename: 文件名:return: response"""# 防止目录遍历漏洞path = posixpath.normpath(filename).lstrip('/')# 拼接文件路径 document_root: 文件根路径(自己设置,例如:/home/files/)full_path = safe_join(document_root, path)if os.path.exists(full_path):stat_obj = os.stat(full_path)# 获取文件类型content_type, encoding = mimetypes.guess_type(full_path)content_type = content_type or 'application/octet-stream'# 计算读取文件的起始位置start_bytes = re.search(r'bytes=(\d+)-(\d+)', request.META.get('HTTP_RANGE', ''), re.S)# 重新计算文件起始位置,切分bytes=0-32768(bytes=temp_size-pos)pos = start_bytes.group().split('=')[1] if start_bytes else 0# 应该取temp_size为起始位置,不然始终差了32k,导致客户端一直下载不完start_bytes = int(pos.split('-')[0]) if pos else 0current_pos = int(pos.split('-')[1]) if pos else 0# 打开文件并移动下标到七十五i之,客户端继续下载时,从上次断开的点继续读取the_file = open(full_path, 'rb')the_file.seek(start_bytes, os.SEEK_SET)# status=200表示下载开始,status=206表示下载暂停后继续,为了兼容火狐浏览器而区分两种状态response = FileResponse(the_file, content_type=content_type, status=206 if start_bytes > 0 else 200)# 修改文件生成器返回指定大小content_length = current_pos - start_bytesresponse = FileResponse(down_file_iterator(full_path, start_bytes, content_length), content_type=content_type, status=206 if start_bytes > 0 else 200)# 这里的‘Content-Length’表示剩余待传输的文件字节长度if stat.S_ISREG(stat_obj.st_mode):# 计算每一包的content-length,如果返回指定大小,每包只能下载指定大小,根据客户端的请求返回指定大小response['Content-Length'] = content_lengthif encoding:response['Content-Encoding'] = encoding# ‘Content-Range’的'/'之前描述响应覆盖的文件字节范围,起始下标为0,'/'之后描述整个文件长度,与'HTTP_RANGE'对应使用response['Content-Range'] = f'bytes {start_bytes}-{stat_obj.st_size-1}/{stat_obj.st_size}'return responseelse:return JsonResponse({'code': 404, 'msg': 'File Not Found'})

client 端代码如下:

import os
import sysimport requestsdef download_file(url, file_path, file_size):"""请求url将文件保存至file_path:param url: 下载路径:param file_path: 文件保存路径:param file_size: 文件大小(也可以用head或者get去获取大小):return:"""if os.path.exists(file_path):os.remove(file_path)sys.stdout.write("\r[%s%s]%d%%" % ('█' * 0, '' * 50, 0))sys.stdout.flush()total_size = int(file_size)temp_size = 0while True:# 请求网址,加入请求头if (total_size - temp_size) < 32 * 1024:pos = total_sizeelse:# 每包下载32k这个可以根据自己的网络环境来设置pos = temp_size + 32 * 1024headers = {'Range': f'bytes={temp_size}-{pos}'}response = requests.get(url, stream=True, verify=False, headers=headers)if response.status_code == 200 or response.status_code == 206:with open(file_path, 'ab') as f:f.write(response.content)# 终端显示进度temp_size = os.path.getsize(file_path) if os.path.exists(file_path) else 0done = int(50 * temp_size / total_size)sys.stdout.write("\r[%s%s]%d%%" % ('█' * done, '' * (50 - done), 100 * temp_size / total_size))sys.stdout.flush()# 下载完成退出if temp_size == total_size:breakreturn file_path

下载测试文件

测试结果:
下载后暂停继续下载成功,每包下载32k这个可以根据自己的网络环境来设置

下载保存路径

Python Django断点下载(服务端/客户端)相关推荐

  1. TCP聊天文件服务器v2.2 - 服务端客户端套接字解决分包/粘包问题 - SocketQueue继承以及减少冗余

    TCP聊天+传输文件服务器服务器套接字v2.2 整个图当封面吧 所有版本记录: v1.0 : TCP聊天服务器套接字|PyQt5+socket(TCP端口映射+端口放行)+logging+Thread ...

  2. 一个简单的完成端口(服务端/客户端)类

    一个简单的完成端口(服务端/客户端)类 作者:spinoza 翻译:麦子芽儿, POWERCPP(后面部分内容) 下载源代码 原文网址:http://www.codeproject.com/KB/IP ...

  3. mc正版mite服务器,MITE-R192服务端客户端 配套独立可管理登陆系统

    MITE由于其特殊性不可安装任何MOD插件,所以除了开通正版验证一直很少有其他ID登陆方案.这里放出包含了由@yushijinhun提供的authlib-agent 开源的游戏外登录解决方案的MITE ...

  4. TCP/IP网络编程之基于TCP的服务端/客户端(一)

    TCP/IP网络编程之基于TCP的服务端/客户端(一) 理解TCP和UDP 根据数据传输方式的不同,基于网络协议的套接字一般分为TCP套接字和UDP套接字.因为TCP套接字是面向连接的,因此又称为基于 ...

  5. 我的世界服务器java出错_看懂 游戏《Minecraft》的崩溃报告 服务端/客户端

    前言 [WARNING] 本篇适用于Minecraft服务端/客户端报错的诊断分析,其他游戏除外,比如网易 [WARNING] 本篇适用于Minecraft服务端/客户端报错的诊断分析,其他游戏除外, ...

  6. 魔力宝贝服务器修改器装备,【法兰城的回忆】魔力宝贝游戏手工架设服务端+客户端修改工具+流程说明...

    [法兰城的回忆]魔力宝贝游戏手工架设服务端+客户端修改工具+流程说明_站长网(Downzz.com) 安装说明: 1.安装宝塔 yum install -y wget && wget ...

  7. 网络云盘项目——HTTP接口介绍、功能介绍、服务端/客户端代码解析

    一.本文目的 本项目分为6篇博客文章完成: 1.项目总体介绍:https://blog.csdn.net/qq_41453285/article/details/107871393. 2.Redis部 ...

  8. Discuz!论坛助手 [原创] [服务端+客户端]插件 人气提升/贴子监控/批量顶贴/来贴提醒/编辑辅助工具 1.0(附演示)

    Discuz!插件 插件名称 论坛助手1.0 适用版本 Discuz!7.0 语言编码 GBK  插件作者 vcmain 版权所属 免费绿色.版权归作者所有 支持网站 http://www.vcmai ...

  9. 侠义道服务器修改,[侠义道1]1.85虚拟机镜像一键启动服务端+客户端+启动教程+元宝修改...

    [侠义道1]1.85虚拟机镜像一键启动服务端+客户端+启动教程+元宝修改 资源说明: 1.本资源为侠义道Online1.85版本,默认IP为192.168.200.175,虚拟机架设,默认使用NAT模 ...

最新文章

  1. 群同态基本定理证明_近世代数(3)——群的基本性质
  2. apache开启.htaccess
  3. MySQL基础学习过程
  4. 打针小说软件测试,UPDATE注射(mysql+php)的两个模式
  5. 毕业十年|我的嵌入式AI学习路线(笔记、代码)
  6. 2016版excel_憋了三年,整理出这些相见恨晚的Excel大神技巧,分分钟做出超赞Excel表格!...
  7. 2020 OPPO 开发者大会重磅推出 ColorOS 11,适配机型达 33 种
  8. PPDuck3 for Mac(pp鸭图片批量压缩工具)最新官方版免下载
  9. 【PHP代码审计】 那些年我们一起挖掘SQL注入 - 4.全局防护Bypass之二次注入
  10. 安卓简单实现百度地图
  11. Qt_屏幕保护程序、进程监听、数据库读取、屏幕保护
  12. 控制工程——传递函数与状态方程
  13. 【npm i 报错解决方法】npm ERR! code ERESOLVEnpm ERR!npm ERR! While resolving: by-web@1.2.2npm ERR!
  14. xargs 重定向输出 示列分析
  15. Android studio 写xml的不能自动补全的问题
  16. python numpy.arry, pytorch.Tensor及原生python中list相互转换
  17. 使用GitHub托管网站,自定义域名
  18. 唯品会、滴滴、沪江架构师,关于微服务粒度、高可用、持续交互的实践分享交流(下)...
  19. K-means均值聚类算法python代码实现
  20. php接口(api)

热门文章

  1. 数据结构-1.单链表的初始化
  2. [转载备用]极酷SevenColorPlayer网页播放器(炫彩广告版),最强播放器定制
  3. 完美解决Pytorch在Pycharm没有代码提示的问题
  4. java正则表达式语法(java正整数正则表达式)
  5. 7-5 病毒溯源 (25 分)
  6. 电磁兼容常用名词术语
  7. Small Tip: 怎么去Schedule一个Analysis for Office的workbook
  8. oracle创建索引占用表空间吗,oracle创建索引表空间
  9. 我与我的专业计算机作文500字,我的理想作文500字——电脑高手
  10. pandas | DataFrame基础运算以及空值填充