Discord Bot替换requests为aiohttp后无法复用已下载图片如何解决?
错误原因
你遇到的ValueError: I/O operation on closed file报错,核心原因是:aiohttp.FormData在发送包含BytesIO的请求时,会读取完流内全部内容,并且在请求结束后自动关闭该IO流,因此后续再尝试读取同一个image对象时就会触发已关闭的报错。
解决方案
最稳妥的方式是先存储下载得到的图片原始二进制数据,需要使用时再基于原始数据新建BytesIO对象,全程不需要重复下载图片:
import io import aiohttp import discord async def post_image(url, user, nick): debug = client.get_channel(channels[1]) async with aiohttp.ClientSession() as session: # 下载图片并存储原始二进制 async with session.get(url) as response: image_raw = await response.read() # 构造POST请求参数 data = aiohttp.FormData() # 注意这里字段名要和你原有requests逻辑的字段对应,原逻辑用的是image不是file data.add_field('image', io.BytesIO(image_raw), filename='blank.png') data.add_field('user', str(user)) data.add_field('nick', str(nick)) # 请求外部接口获取结果 async with session.post(image_host, data=data) as info: result = await info.text() # 用原始二进制新建BytesIO发送到Discord await debug.send(result, file=discord.File(io.BytesIO(image_raw), 'blank.png'))
如果你不想多次创建BytesIO对象,也可以在POST请求结束后手动重置流指针,但该方法存在版本兼容风险(部分版本aiohttp会强制关闭流),仅作参考:
# 下载后创建BytesIO image = io.BytesIO(await response.read()) # ... 构造FormData发送请求 ... # 请求结束后重置指针到流开头 image.seek(0) # 再发送到Discord await debug.send(result, file=discord.File(image, 'blank.png'))
额外注意
你原有aiohttp代码里给FormData加的图片字段名是file,但你原先的requests逻辑里用的字段名是image,如果不修改字段名,会导致外部接口接收不到图片参数,返回异常结果。
内容的提问来源于stack exchange,提问作者Stevy
相关产品推荐
相关产品推荐

