Как я могу загружать файлы через aiohttp, используя ответ на запрос на получение?
Для начала я пишу асинхронную оболочку для WordPress REST API. У меня есть сайт Wordpress, размещенный на Bluehost. Я работаю с конечной точкой для загрузки мультимедиа (изображений). Мне удалось загрузить изображение, но я хотел бы внести два изменения. Второе изменение - это то, что я действительно хочу, но из любопытства я также хотел бы знать, как реализовать изменение 1. Сначала я предоставлю код, а затем некоторые детали.
Рабочий код
async def upload_local_pic2(self, local_url, date, title):
url = f'{self.base_url}/wp-json/wp/v2/media'
with aiohttp.MultipartWriter() as mpwriter:
json = {'title': title, 'status':'publish'}
mpwriter.append_json(json)
with open(local_url, 'rb') as f:
print(f)
payload = mpwriter.append(f)
async with self.session.post(url, data=payload) as response:
x = await response.read()
print(x)
Изменить 1
Первое изменение заключается в загрузке с использованием aiofiles.open() вместо использования open (), поскольку я ожидаю обработки большого количества файлов. Следующий код не работает.
async def upload_local_pic(self, local_url, date, title):
url = f'{self.base_url}/wp-json/wp/v2/media'
with aiohttp.MultipartWriter() as mpwriter:
json = {'title': title, 'status':'publish'}
mpwriter.append_json(json)
async with aiofiles.open(local_url, 'rb') as f:
print(f)
payload = mpwriter.append(f)
async with self.session.post(url, data=payload) as response:
x = await response.read()
print(x)
Изменить 2
Другое мое изменение заключается в том, что я хотел бы иметь еще одну функцию, которая может загружать файлы непосредственно на сервер WordPress, не загружая их локально. Поэтому вместо получения локального изображения я хочу передать URL-адрес изображения в Интернете. Следующий код также не работает.
async def upload_pic(self, image_url, date, title):
url = f'{self.base_url}/wp-json/wp/v2/media'
with aiohttp.MultipartWriter() as mpwriter:
json = {'title':title, 'status':'publish'}
mpwriter.append_json(json)
async with self.session.get(image_url) as image_response:
image_content = image_response.content
print(image_content)
payload = mpwriter.append(image_content)
async with self.session.post(url, data = payload) as response:
x = await response.read()
print(x)
Детали / Отладка
Я пытаюсь понять, почему каждый из них не работает. Я думаю, что главное - это призывы к
print(image_content)
и
print(f)
которые показывают, что именно я ввожу
mpwriter.append
В примере, который работает, я просто использую стандартный Python
open()
функция, я, по-видимому, передаю
<_io.BufferedReader name='/redactedfilepath/index.jpeg'>
В примере с изменением 1 с aiofile я передаю
<aiofiles.threadpool.binary.AsyncBufferedReader object at 0x7fb803122250>
Wordpress вернет этот html:
b'<head><title>Not Acceptable!</title></head><body><h1>Not Acceptable!</h1><p>An appropriate representation of the requested resource could not be found on this server. This error was generated by Mod_Security.</p></body></html>'
И, наконец, в изменении 2, где я пытаюсь передать то, что дает мне запрос на получение URL, я получаю<StreamReader 292 bytes>
. Ответ WordPress такой же, как и выше для Mod Security.
Есть идеи, как заставить эти примеры работать? Похоже, что все они являются своего рода читателем io, но я предполагаю, что основной код aiohttp обрабатывает их по-разному.
Также это не должно иметь значения, но это URL-адрес, который я передаю в пример изменения 2.
2 ответа
Итак, я понял оба изменения.
Для первого изменения при попытке прочитать файл с
aiofiles
, Мне нужно просто прочитать весь файл, а не передавать его в обработчике файлов. Кроме того, мне нужно настроить размещение контента вручную.
async def upload_local_pic(self, local_url, date, title):
url = f'{self.base_url}/wp-json/wp/v2/media'
with aiohttp.MultipartWriter() as mpwriter:
json = {'status':'publish'}
mpwriter.append_json(json)
async with aiofiles.open(local_url, mode='rb') as f:
contents = await f.read()
payload = mpwriter.append(contents)
payload.set_content_disposition('attachment', filename= title+'.jpg')
async with self.session.post(url, data=payload) as response:
x = await response.read()
print(x)
Что касается второго изменения, это аналогичная концепция с простой загрузкой файла непосредственно с URL-адреса. Вместо того, чтобы передавать обработчик, который будет читать контент, мне нужно сначала прочитать весь контент. Мне также нужно вручную установить content-disposition.
async def upload_pic(self, image_url, date, title):
url = f'{self.base_url}/wp-json/wp/v2/media'
with aiohttp.MultipartWriter() as mpwriter:
json = {'status':'publish'}
mpwriter.append_json(json)
async with self.session.get(image_url) as image_response:
image_content = await image_response.read()
payload = mpwriter.append(image_content)
payload.set_content_disposition('attachment', filename=title+'.jpg')
async with self.session.post(url, data = payload) as response:
x = await response.read()
print(x)
Отвечу только на заголовок поста (а не на вопросы, которые между ними).
Следующий код должен дать краткий пример того, как загрузить файл с URL № 1 на URL № 2 (без необходимости загружать файл на локальный компьютер и только затем выполнять загрузку).
Я приведу здесь два примера:
- Прочитать все содержимое файла в память (не скачивая). Это, конечно, не так хорошо при работе с огромными файлами...
- Прочитайте и отправьте файл порциями (поэтому мы не будем читать все содержимое файла сразу).
Пример №1: Чтение всего содержимого файла СРАЗУ и загрузка
import asyncio
import aiohttp
async def http_upload_from_url(src, dst):
async with aiohttp.ClientSession() as session:
src_resp = await session.get(src)
#print(src_resp)
dst_resp = await session.post(dst, data=src_resp.content)
#print(dst_resp)
try:
asyncio.run(http_upload_from_url(SRC_URL, DST_URL))
except Exception as e:
print(e)
Пример #2: Чтение содержимого файла ПЛОЩАДКАМИ и загрузка
import asyncio
import aiohttp
async def url_sender(url=None, chunk_size=65536):
async with aiohttp.ClientSession() as session:
resp = await session.get(url)
#print(resp)
async for chunk in resp.content.iter_chunked(chunk_size):
#print(f"send chunk with size {len(chunk)}")
yield chunk
async def chunked_http_upload_from_url(src, dst):
async with aiohttp.ClientSession() as session:
resp = await session.post(dst, data=url_sender(src))
#print(resp)
#print(await resp.text())
try:
asyncio.run(chunked_http_upload_from_url(SRC_URL, DST_URL))
except Exception as e:
print(e)
Некоторые примечания:
- Вам необходимо определить SRC_URL и DST_URL.
- Я добавил отпечатки только для отладки (на случай, если вы не получите ответ [200 OK]).