25

So I have a discord bot that I'm playing with to learn Python. I have a command that downloads images, and edits/merges them, then sends the edited image to chat. I was using requests to do this before, but I was told by one of the library devs for discord.py that I should be using aiohttpinstead of requests. I can't find how to download images in aiohttp, I've tried a bunch of stuff, but none of it works.

if message.content.startswith("!async"):
    import aiohttp
    import random
    import time
    import shutil
    start = time.time()
    notr = 0
    imagemake = Image.new("RGBA",(2048,2160))
    imgsave = "H:\Documents\PyCharmProjects\ChatBot\Images"
    imagesend = os.path.join(imgsave,"merged.png")
    imgmergedsend =os.path.join(imgsave,"merged2.png")
    with aiohttp.ClientSession() as session:
        async with session.get("http://schoolido.lu/api/cards/788/") as resp:
            data = await resp.json()
            cardsave = session.get(data["card_image"])
            with open((os.path.join(imgsave, "card.png")),"wb") as out_file:
                shutil.copyfileobj(cardsave, out_file)

is what I have right now, but that still doesn't work.

So, is there a way to download images?

link2110
  • 471
  • 1
  • 4
  • 7
  • You have missed `await` before `session.get(data["card_image"])`. Also `cardsave` is a response, not file-like object. You should copy bytes body from response to file manually. – Andrew Svetlov Feb 15 '16 at 08:46

3 Answers3

43

You lock loop when write file. You need use aiofiles.

import aiohttp        
import aiofiles

async with aiohttp.ClientSession() as session:
    url = "http://host/file.img"
    async with session.get(url) as resp:
        if resp.status == 200:
            f = await aiofiles.open('/some/file.img', mode='wb')
            await f.write(await resp.read())
            await f.close()
Alexey Panevin
  • 597
  • 5
  • 11
  • While methods read(), json() and text() are very convenient you should use them carefully. All these methods load the whole response in memory. – falselight Jan 05 '23 at 15:34
  • 1
    You can also use `async with` instead of opening and closing the file. E.g. ```async with aiofiles.open(path, 'wb') as f: await f.write(await resp.read())``` – Danferno Feb 16 '23 at 15:08
9

So I figured it out, a while ago:

if message.content.startswith("!async2"):
    import aiohttp
    with aiohttp.ClientSession() as session:
        async with session.get("http://schoolido.lu/api/cards/788/") as resp:
            data = await resp.json()
            card = data["card_image"]
            async with session.get(card) as resp2:
                test = await resp2.read()
                with open("cardtest2.png", "wb") as f:
                    f.write(test)

I was getting a response, not an image response

link2110
  • 471
  • 1
  • 4
  • 7
5
pdf_url = 'https://example.com/file.pdf'
    
async with aiohttp.ClientSession() as session:
    async with session.get(pdf_url) as resp:
        if resp.status == 200:
            with open('file.pdf', 'wb') as fd:
                async for chunk in resp.content.iter_chunked(10):
                    fd.write(chunk)

While methods read(), json() and text() are very convenient you should use them carefully. All these methods load the whole response in memory. For example if you want to download several gigabyte sized files, these methods will load all the data in memory. Instead you can use the content attribute. It is an instance of the aiohttp.StreamReader class. The gzip and deflate transfer-encodings are automatically decoded for you:

https://docs.aiohttp.org/en/stable/client_quickstart.html#streaming-response-content

falselight
  • 527
  • 7
  • 11