Как я могу загружать файлы через 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. Прочитать все содержимое файла в память (не скачивая). Это, конечно, не так хорошо при работе с огромными файлами...
  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]).
Другие вопросы по тегам