You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

将Python同步图片下载改为异步(aiohttp)的问题排查

解决aiohttp异步获取图片转PNG bytes的报错问题

问题根源

你碰到的TypeError: unhashable type: 'JpegImageFile',多半是两个原因:

  • 误把PIL返回的JpegImageFile对象用作了需要哈希的场景(比如字典键、集合元素)
  • aiohttp获取响应内容的逻辑和requests不同,直接操作response.content没正确读取字节数据

修正后的异步实现代码

先看可运行的完整代码,再拆解关键步骤:

import asyncio
import aiohttp
from io import BytesIO
from PIL import Image

async def fetch_and_convert_to_png_bytes(url):
    async with aiohttp.ClientSession() as session:
        async with session.get(url) as response:
            # 异步读取完整响应字节(和requests的response.content不同,必须用await)
            image_bytes = await response.read()
            # 用BytesIO包装字节流供PIL读取
            img = Image.open(BytesIO(image_bytes))
            # 转换为PNG格式并写入BytesIO
            png_buffer = BytesIO()
            img.save(png_buffer, format='PNG')
            # 重置缓冲区指针到开头,读取最终的bytes对象
            png_buffer.seek(0)
            return png_buffer.getvalue()

async def main():
    # 替换成你的图片URL列表
    urls = ["https://example.com/test1.jpg", "https://example.com/test2.png"]
    tasks = [fetch_and_convert_to_png_bytes(url) for url in urls]
    # 批量异步执行
    png_bytes_list = await asyncio.gather(*tasks)
    # 验证结果
    for idx, bytes_data in enumerate(png_bytes_list):
        print(f"第{idx+1}张图片的PNG字节长度: {len(bytes_data)}")

if __name__ == "__main__":
    asyncio.run(main())

核心修正点

  • 正确读取aiohttp响应:aiohttp的response.content是异步迭代器,必须用await response.read()获取完整字节,不能直接像requests那样直接取response.content
  • 避免PIL对象误用:不要把Image.open()返回的图片对象(比如JpegImageFile)放到需要哈希的容器里,比如别写{img: "some_value"}这种代码
  • 保持PNG转换逻辑一致:和同步代码逻辑对齐,用BytesIO做中间容器,完成格式转换后再导出为bytes对象

排查要点

如果还是报错,检查这几点:

  • 确认响应状态码是200,避免拿到错误响应的无效字节
  • 确保图片URL可正常访问,没有被反爬拦截导致内容异常
  • 批量处理时复用ClientSession,不要在每个请求里重复创建session(代码里已经正确实现了复用)

内容的提问来源于stack exchange,提问作者The Dan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.08 01:40:34