Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Upload multipart/form-data from memory in Python with aiohttp

I am trying to work with Discord.py to upload an attachment to Ballchasing's API. Here are the relevant API sections:

https://discordpy.readthedocs.io/en/latest/api.html#discord.Attachment.read

https://ballchasing.com/doc/api#upload-upload-post

The example in the docs suggest using requests, but I've read over and over again that this isn't best practice for a Discord bot because you want asynchronous code to avoid anything that could block the execution of your script.

Here is what I have:

@commands.Cog.listener()        
async def on_message(self, message):
    headers = {'Authorization':self.upload_key_bc}
    for attachment in message.attachments:
        file = io.BytesIO(await attachment.read())
        action = {'file': ('replay.replay', file.getvalue())}
        async with aiohttp.ClientSession() as session:
            async with session.post(self.api_upload_bc, headers=headers, data=action) as response:
                print(response.status)
                print(await response.text())

I'm getting this response:

failed to get multipart form: request Content-Type isn't multipart/form-data

I tried forcing the Content-Type header to multiparth/form-data and I get a different error:

failed to get multipart form: no multipart boundary param in Content-Type

I think the way I'm sending the data is the problem. What am I missing?

like image 602
dan Avatar asked Oct 23 '25 14:10

dan


1 Answers

Manually create a FormData object and specify the filename explicitly to enable multipart/form-data.

If digging into the code, {'file': ('replay.replay', file.getvalue())} is treated as non-special value in FormData and render as str(('replay.replay', file.getvalue()) in urllib.parse.urlencode().

from aiohttp import FormData

@commands.Cog.listener()
async def on_message(self, message):
    headers = {'Authorization': self.upload_key_bc}
    for attachment in message.attachments:
        
        formdata = FormData()
        formdata.add_field('file', BytesIO(await attachment.read()), filename='replay.replay')

        async with aiohttp.ClientSession() as session:
            async with session.post(self.api_upload_bc, headers=headers, data=formdata) as response:
                print(response.status)
                print(await response.text())

like image 116
Aaron Avatar answered Oct 26 '25 05:10

Aaron



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!