如何优化Python实现30MB图片的毫秒级下载?
针对大尺寸图片高效下载的优化方案
现有requests代码的改进点
- 流式下载+分块写入:避免一次性把30MB内容加载到内存,边下载边写入磁盘,减少内存占用同时提升写入效率。示例代码:
import requests import os def download_image(url, save_folder): os.makedirs(save_folder, exist_ok=True) filename = url.split("/")[-1] save_path = os.path.join(save_folder, filename) # 使用Session复用连接 with requests.Session() as session: # 流式获取响应 with session.get(url, stream=True, timeout=10) as r: r.raise_for_status() # 设置合适的块大小 with open(save_path, 'wb') as f: for chunk in r.iter_content(chunk_size=16384): if chunk: f.write(chunk) - 复用TCP连接:用
requests.Session()替代单次请求,复用底层TCP连接,减少TCP握手的时间开销。 - 添加重试机制:针对网络波动设置自动重试,避免单次请求失败中断流程:
from requests.adapters import HTTPAdapter from urllib3.util.retry import Retry def create_session(): session = requests.Session() retry = Retry(total=3, backoff_factor=0.5, status_forcelist=[500, 502, 503, 504]) adapter = HTTPAdapter(max_retries=retry) session.mount('http://', adapter) session.mount('https://', adapter) return session - 并行下载:用多线程处理多个图片URL,避免单线程等待串行执行:
from concurrent.futures import ThreadPoolExecutor if __name__ == "__main__": image_urls = ["https://example.com/image1.jpg", "https://example.com/image2.jpg"] save_folder = "downloaded_images" with ThreadPoolExecutor(max_workers=4) as executor: executor.map(lambda url: download_image(url, save_folder), image_urls)
高效的替代库与方法
- aiohttp(异步下载):适合高并发场景,基于异步IO模型,比多线程更少资源开销,示例:
import aiohttp import asyncio import os async def download_image_async(url, save_folder): os.makedirs(save_folder, exist_ok=True) filename = url.split("/")[-1] save_path = os.path.join(save_folder, filename) async with aiohttp.ClientSession() as session: async with session.get(url, timeout=10) as r: r.raise_for_status() with open(save_path, 'wb') as f: while chunk := await r.content.read(16384): f.write(chunk) async def main(): image_urls = ["https://example.com/image1.jpg", "https://example.com/image2.jpg"] save_folder = "downloaded_images" tasks = [download_image_async(url, save_folder) for url in image_urls] await asyncio.gather(*tasks) if __name__ == "__main__": asyncio.run(main()) - curl-cffi:基于libcurl的绑定,性能优于requests,支持HTTP/2,适合大文件下载。
- wget:专门的下载工具库,内置断点续传、进度条等功能,代码极简:
import wget import os def download_image_wget(url, save_folder): os.makedirs(save_folder, exist_ok=True) wget.download(url, out=save_folder)
通用优化技巧与最佳实践
- 断点续传:检查本地文件大小,发送
Range请求头获取剩余部分,避免重复下载已完成的内容。 - 启用压缩传输:在请求头中添加
Accept-Encoding: gzip, deflate,让服务器返回压缩后的内容,减少传输数据量。 - 本地缓存:对已下载的图片记录URL和本地路径,后续请求优先读取本地文件,避免重复请求服务器。
- 调整块大小:根据磁盘IO性能调整
chunk_size,一般16KB-64KB是比较均衡的选择,过小会增加IO次数,过大则占用更多内存。 - 跳过不必要的处理:下载阶段仅做字节流写入,不要解析图片内容(如PIL打开),避免额外性能开销。
- 选择HTTP/2协议:部分库支持HTTP/2,可复用连接并并行发送请求,提升多文件下载效率。
内容的提问来源于stack exchange,提问作者kairoon bodanapu
相关产品推荐
相关产品推荐

