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?
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())