大文件下载需避免response.read()导致oom,应使用iter_chunked()配合aiofiles.open异步写入;n建议8192或65536;iter_any()适合纯下载但不适用分块编码解析;断点续传等需手动实现。

大文件直接用 response.read() 会 OOM
调用 response.read() 会把整个响应体一次性读进内存。下载一个 2GB 的 ISO 文件,Python 进程内存占用就飙升到 2GB+,在 4GB 内存的 VPS 上几乎必然被 Linux OOM Killer 杀掉。这不是“慢”,而是根本跑不下去。
iter_chunked() 不等于流式落盘,关键在写入方式
很多人以为写了 async for chunk in resp.content.iter_chunked(8192) 就安全了——错。如果后面用内置 open() 写文件,f.write(chunk) 是同步阻塞操作,会卡住事件循环;更糟的是,若你把所有 chunk 累加到一个 bytes 对象里再写,和 read() 没区别。
- 必须用
aiofiles.open(..., 'wb')替代open() -
await f.write(chunk)才真正异步、不阻塞、不累积内存 -
iter_chunked(n)的n建议保持 8192 或 65536,别设成 1MB——单次分配过大反而易触发 GC 暂停或分配失败
别混淆 iter_any() 和 iter_chunked()
两者都返回异步迭代器,但语义不同:
-
iter_chunked(8192):严格按 8192 字节切块(最后一块可能更小),适合对齐存储或校验 -
iter_any():只管吐当前 TCP buffer 里有的数据,块大小不固定,开销略低,纯下载场景够用 - 别用
iter_any()却手动解析 HTTP 分块编码(Transfer-Encoding: chunked)——那是服务器的事,客户端不该碰
断点续传、超时、重试这些事,aiohttp 一概不管
aiohttp 只负责把字节从 socket 搬出来,不封装业务逻辑。下载中途断了?它不会自动重连、不会查 Content-Range、不会帮你维护已写 offset。要续传,得自己:
- 先
HEAD请求查Accept-Ranges: bytes和Content-Length - 检查本地文件大小,算出已下载字节数
- 下次
GET时带Range: bytes={offset}-头 - 用
aiofiles.open(..., 'rb+')打开文件,seek()到 offset 后写入
真正难的不是读,是写完怎么保证原子性、怎么处理并发覆盖、怎么校验每一块——这些都得手撸,没现成轮子。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











