如何使用httpx流式下载大文件并直接写入S3,无需本地存储?
流式下载文件直接上传到S3实现方案
结论
完全可以实现文件流直接上传到S3,无需落地本地磁盘。你可以使用boto3 S3客户端的upload_fileobj方法,直接接收流式下载的字节块进行上传,同时可以保留原有的进度条显示逻辑。
前置依赖
你需要安装如下Python库:
pip install httpx boto3 tqdm
实现代码
import httpx import boto3 from tqdm import tqdm # 初始化S3客户端,会自动读取环境内的AWS凭证 s3 = boto3.client('s3') # 替换为你自己的配置 BUCKET_NAME = "你的目标S3桶名" OBJECT_KEY = "存储到S3的文件路径/100MB.bin" DOWNLOAD_URL = "https://speed.hetzner.de/100MB.bin" with httpx.stream("GET", DOWNLOAD_URL) as response: # 请求失败直接抛出异常,避免上传错误内容 response.raise_for_status() total_size = int(response.headers["Content-Length"]) # 初始化进度条 pbar = tqdm(total=total_size, unit_scale=True, unit_divisor=1024, unit="B") # 自定义可读类,适配boto3的文件读取接口,同时更新进度条 class ProgressStreamReader: def __init__(self, response): self._chunk_iter = response.iter_bytes(chunk_size=1024*1024) self._buffer = b'' def read(self, read_size=-1): # 按需读取字节块 while len(self._buffer) < read_size or read_size == -1: try: chunk = next(self._chunk_iter) except StopIteration: break self._buffer += chunk pbar.update(len(chunk)) if read_size == -1: return_data = self._buffer self._buffer = b'' else: return_data = self._buffer[:read_size] self._buffer = self._buffer[read_size:] return return_data # 直接上传流到S3,无本地磁盘写入操作 s3.upload_fileobj( Fileobj=ProgressStreamReader(response), Bucket=BUCKET_NAME, Key=OBJECT_KEY, # 可选配置:保留源文件的Content-Type元数据 ExtraArgs={'ContentType': response.headers.get('Content-Type', 'application/octet-stream')} ) pbar.close()
注意事项
- AWS凭证无需硬编码到代码中,支持以下任意一种配置方式:
- 环境变量
AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY - 本地
~/.aws/credentials配置文件 - AWS运行环境(EC2、ECS、Lambda等)绑定的IAM角色,需要给角色开通对应S3桶的写入权限
- 环境变量
upload_fileobj默认自动处理大文件分块上传:文件大小超过8MB时自动拆分多块并行上传,自带失败重试逻辑,无需手动实现分块逻辑- 如果下载源没有返回
Content-Length响应头,删除tqdm的total参数即可,进度条会自动适配无总大小的场景
内容的提问来源于stack exchange,提问作者ca9163d9
相关产品推荐
相关产品推荐

