Ошибка - "попытка прочитать закрытый файл"
Всем доброго времени суток. В данном коде присутствует работа с изображением в переменной data идет создание даты для пост запроса, файлы можно только один раз читать я это понял, но я не понял, почему ошибка не ушла, когда я в коде создал еще 1 дату?
async def _upload_file_to_vk(self, upload_url, data):
async with self.manager.session.post(upload_url, data=data) as resp:
if resp.status != 200:
return None
upload_result_text = await resp.text()
if not upload_result_text:
return None
try:
upload_result = json.loads(upload_result_text)
if "error" in upload_result:
raise RuntimeError
except RuntimeError:
return None
return upload_result
async def upload_photo(self, file, **kwargs):
if kwargs.get("peer_id") is None:
peer_id = self.peer_id
else:
peer_id = kwargs.get("peer_id")
upload_data = await self.manager.request(
"photos.getMessagesUploadServer", peer_id=peer_id
)
if upload_data.error:
return None
upload_url = upload_data.response["upload_url"]
data = aiohttp.FormData()
data.add_field("photo", file, filename="image.png")
upload_result = await self._upload_file_to_vk(upload_url, data)
if not upload_result:
return None
attachments = await self.manager.request(
"photos.saveMessagesPhoto", **upload_result
)
#Если произошла ошибка пробуем загрузить тот же самый аттач без peer_id
if attachments.error:
upload_data = await self.manager.request(
"photos.getMessagesUploadServer", peer_id=None
)
if upload_data.error:
return None
upload_url = upload_data.response["upload_url"]
data = aiohttp.FormData()
data.add_field("photo", file, filename="image.png")
upload_result = await self._upload_file_to_vk(upload_url, data)
if not upload_result:
return None
attachments = await self.manager.request(
"photos.saveMessagesPhoto", **upload_result
)
if attachments.error:
return None
return self.manager.create_attachment(
attachments.response[0], "photo"
)
f = io.BytesIO()
img2.save(f, format='png')
f.seek(0)
at = await env.upload_photo(f)
f.close()
Получаю ошибку:
File "plugins/content/certificate.py", line 50, in certificate
at = await env.upload_photo(f)
File "/root/grizzly/kutana/manager/vk/environment.py", line 146, in upload_photo
upload_result = await self._upload_file_to_vk(upload_url, data)
File "/root/grizzly/kutana/manager/vk/environment.py", line 13, in _upload_file_to_vk
async with self.manager.session.post(upload_url, data=data) as resp:
File "/usr/local/lib/python3.6/dist-packages/aiohttp/client.py", line 1005, in __aenter__
self._resp = await self._coro
File "/usr/local/lib/python3.6/dist-packages/aiohttp/client.py", line 466, in _request
ssl=ssl, proxy_headers=proxy_headers, traces=traces)
File "/usr/local/lib/python3.6/dist-packages/aiohttp/client_reqrep.py", line 294, in __init__
self.update_body_from_data(data)
File "/usr/local/lib/python3.6/dist-packages/aiohttp/client_reqrep.py", line 479, in update_body_from_data
body = body()
File "/usr/local/lib/python3.6/dist-packages/aiohttp/formdata.py", line 148, in __call__
return self._gen_form_data()
File "/usr/local/lib/python3.6/dist-packages/aiohttp/formdata.py", line 142, in _gen_form_data
self._writer.append_payload(part)
File "/usr/local/lib/python3.6/dist-packages/aiohttp/multipart.py", line 798, in append_payload
size = payload.size
File "/usr/local/lib/python3.6/dist-packages/aiohttp/payload.py", line 360, in size
position = self._value.tell()
ValueError: I/O operation on closed file.
Не понимаю, что я делаю не так?
Ответы (2 шт):
Вы не так поняли. Файлы можно читать неограниченное кол-во раз, но после каждого прочтения файла следует его закрывать, т.к. нередко Python начинает кричать неблагим матом и выводить кракозябры. Также возможно что вы не открыли нужный файл (самое первое обращение, например при работе с txt: t = open('myfile.txt')), либо прописали неправильное название. Вполне возможно что он пытается обратиться к файлу в совершенно другой директории (отличной от расположения вашего файла), если найду пример правильного обращения к файлу, то вставлю сюда.
Вам надо прописать:
import os #В начале файла
data = aiohttp.FormData()
data.add_field("photo", file, filename=os.path.dirname(os.path.abspath(__file__))+"/имя_файла.расширение") #Таким образом файл будет искаться в одной директории с кодом.
Также вам нужно f.close() использовать в другом месте, после вызова файла в двух местах внутри функции. Так вам не придётся закрывать файл вручную.
По результатам обсуждения в комментариях пришли к выводу, что при POST запросе отправляемый файловый объект закрывается. Поэтому такой вариант: пишем изображение в объект BytesIO, но в upload_photo передаем не этот же объект, а данные из него (получаем их с помощью метода getvalue). Внутри upload_photo перед каждой отправкой данных заново создаем объект BytesIO.
async def upload_photo(self, file_data: bytes, **kwargs):
# ...
data = aiohttp.FormData()
data.add_field("photo", io.ByteIO(file_data), filename="image.png")
upload_result = await self._upload_file_to_vk(upload_url, data)
if not upload_result:
return None
attachments = await self.manager.request(
"photos.saveMessagesPhoto", **upload_result
)
#Если произошла ошибка пробуем загрузить тот же самый аттач без peer_id
if attachments.error:
# ...
data = aiohttp.FormData()
data.add_field("photo", io.BytesIO(file_data), filename="image.png")
upload_result = await self._upload_file_to_vk(upload_url, data)
# ...
f = io.BytesIO()
img2.save(f, format='png')
at = await env.upload_photo(f.getvalue())
Также можно просто прикреплять вложение как байты, а не как файловый объект:
data.add_field("photo", file_data, filename="image.png")