突破内存限制:Requests流式上传大文件完全指南
【免费下载链接】requests 项目地址: https://gitcode.com/gh_mirrors/req/requests
你是否遇到过上传GB级文件时程序崩溃?是否因服务器超时错误反复重试?Requests流式上传(Streaming Upload)技术能帮你解决这些问题。本文将通过3个实用场景、5段核心代码和2种高级技巧,让你彻底掌握大文件分块上传的实现方法,无需复杂框架即可轻松处理超大型文件传输。
流式上传原理与优势
传统文件上传需要将整个文件读入内存,这会导致内存占用过高和上传超时。而流式上传采用分块传输机制,将文件分割成小块逐步发送,实现"边读边传"的高效模式。
核心优势对比:
| 上传方式 | 内存占用 | 最大支持文件 | 网络容错 |
|---|---|---|---|
| 传统上传 | O(n) | 受内存限制 | 低,中断需重传全部 |
| 流式上传 | O(1) | 无限制 | 高,可断点续传 |
Requests通过src/requests/models.py中的PreparedRequest类实现流式传输,当检测到数据流时自动启用分块编码(Transfer-Encoding: chunked)。
基础实现:文件对象流式上传
最简洁的流式上传方式是直接传递文件对象。Requests会自动处理分块传输,无需手动分割文件:
import requests
def stream_upload_basic(url, file_path):
with open(file_path, 'rb') as f:
# 直接传递文件对象,Requests自动启用流式上传
response = requests.post(
url,
data=f, # 文件对象作为data参数
headers={'Content-Type': 'application/octet-stream'}
)
return response.status_code
# 使用示例:上传10GB视频文件
stream_upload_basic('https://api.example.com/upload', '/data/large_video.mp4')
关键实现原理在src/requests/models.py中:当检测到data参数是可迭代对象且非字符串/列表时,自动启用流式模式,并设置Transfer-Encoding: chunked头部。
高级控制:自定义分块迭代器
对于需要精确控制分块大小或添加进度监控的场景,可以实现自定义迭代器。这种方式允许你控制每个分块的大小、添加校验和或实现上传进度显示:
import requests
from requests.utils import iter_slices # 导入分块工具函数
def file_chunk_iterator(file_path, chunk_size=8192):
"""生成文件分块迭代器,默认8KB分块"""
with open(file_path, 'rb') as f:
while True:
chunk = f.read(chunk_size)
if not chunk:
break
yield chunk # 逐个返回文件分块
def stream_upload_with_progress(url, file_path, chunk_size=8192):
# 创建分块迭代器
file_iterator = file_chunk_iterator(file_path, chunk_size)
# 添加进度监控(可选)
total_size = os.path.getsize(file_path)
uploaded = 0
def progress_hook(chunk):
nonlocal uploaded
uploaded += len(chunk)
progress = (uploaded / total_size) * 100
print(f"Upload progress: {progress:.2f}%", end='\r')
# 发送流式请求
response = requests.post(
url,
data=iter_slices(file_iterator, chunk_size), # 使用分块工具
headers={'Content-Type': 'application/octet-stream'},
hooks={'data': progress_hook} # 注册进度钩子
)
return response.status_code
上述代码使用了src/requests/utils.py中的iter_slices函数,该工具函数负责将迭代器按指定大小分割成块,是实现自定义分块的核心工具。
断点续传:中断恢复机制
网络不稳定时,断点续传功能能节省大量带宽和时间。实现思路是记录已上传位置,中断后从断点继续上传:
import requests
def resume_upload(url, file_path, resume_position=0, chunk_size=8192):
with open(file_path, 'rb') as f:
# 跳转到上次中断位置
f.seek(resume_position)
# 创建带Range头部的请求
headers = {
'Content-Type': 'application/octet-stream',
'Content-Range': f'bytes {resume_position}-*/{os.path.getsize(file_path)}'
}
# 发送剩余部分
response = requests.post(
url,
data=f, # 从当前位置开始读取
headers=headers
)
return response.status_code
断点续传需要服务端支持,客户端实现的关键是通过seek()方法定位文件读取位置,并使用Content-Range头部告知服务端续传位置。
性能优化与最佳实践
分块大小选择
分块大小直接影响传输效率,建议根据网络环境调整:
- 局域网:64KB-1MB
- 互联网:8KB-32KB
- 高延迟网络:1KB-4KB
可通过src/requests/models.py查看Requests默认块大小(CONTENT_CHUNK_SIZE = 10 * 1024,即10KB)。
连接复用
使用Session对象复用TCP连接,减少握手开销:
# 创建持久会话
with requests.Session() as session:
# 会话内所有请求复用连接
response = session.post(url, data=file_object)
错误处理与重试
添加重试机制提高稳定性:
from requests.adapters import HTTPAdapter
from urllib3.util.retry import Retry
def create_retry_session(retries=3, backoff_factor=0.5):
session = requests.Session()
retry_strategy = Retry(
total=retries,
backoff_factor=backoff_factor,
status_forcelist=[429, 500, 502, 503, 504]
)
adapter = HTTPAdapter(max_retries=retry_strategy)
session.mount("https://", adapter)
session.mount("http://", adapter)
return session
# 使用带重试机制的会话上传
session = create_retry_session()
response = session.post(url, data=file_object)
完整案例:大文件上传工具
整合上述技术,实现一个生产级的大文件上传工具:
import os
import requests
from requests.utils import iter_slices
class LargeFileUploader:
def __init__(self, chunk_size=8192, retries=3):
self.chunk_size = chunk_size
self.session = self._create_session(retries)
def _create_session(self, retries):
"""创建带重试机制的会话"""
session = requests.Session()
retry_strategy = requests.adapters.Retry(
total=retries,
backoff_factor=0.5,
status_forcelist=[429, 500, 502, 503, 504]
)
adapter = requests.adapters.HTTPAdapter(max_retries=retry_strategy)
session.mount("https://", adapter)
session.mount("http://", adapter)
return session
def upload(self, url, file_path, progress_callback=None):
"""流式上传文件"""
file_size = os.path.getsize(file_path)
with open(file_path, 'rb') as f:
# 创建分块迭代器
def file_iterator():
while True:
chunk = f.read(self.chunk_size)
if not chunk:
break
if progress_callback:
progress_callback(len(chunk))
yield chunk
# 发送请求
response = self.session.post(
url,
data=iter_slices(file_iterator(), self.chunk_size),
headers={'Content-Type': 'application/octet-stream'}
)
return response.status_code
# 使用示例
if __name__ == "__main__":
uploader = LargeFileUploader(chunk_size=16384) # 16KB分块
# 定义进度回调
total_uploaded = 0
def update_progress(chunk_size):
global total_uploaded
total_uploaded += chunk_size
progress = (total_uploaded / os.path.getsize("large_file.iso")) * 100
print(f"Progress: {progress:.2f}%", end='\r')
# 执行上传
uploader.upload(
"https://api.example.com/upload",
"large_file.iso",
progress_callback=update_progress
)
总结与扩展阅读
流式上传是处理大文件的高效解决方案,通过本文你已掌握:
- 基础流式上传:使用文件对象直接传输
- 高级控制:自定义分块迭代器和进度监控
- 断点续传:中断恢复机制实现
- 性能优化:分块大小调整和连接复用
官方文档:docs/user/advanced.rst中包含更多高级用法。对于需要更复杂功能的场景,可以研究src/requests/adapters.py中的适配器机制,实现自定义传输逻辑。
掌握流式上传技术,让你的应用轻松应对GB级文件传输挑战!
【免费下载链接】requests 项目地址: https://gitcode.com/gh_mirrors/req/requests
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考




