如何通过Python aiohttp ClientSession发布多部分表单数据

11

我正在尝试异步发送一些多部分编码表单数据作为 post 请求,主要是文件和其他两个字段。

在尝试使用 asyncio 之前,我使用 requests-toolbelt 的 MultipartEncoder (https://github.com/requests/toolbelt) 同步执行进程,对于普通请求效果很好,但在使用 aiohttp 进行异步操作时却无法正常工作。aiohttp 提供了两个多部分类,一个是 FormData() 类,另一个是 MultipartWriter() 类,但都没有给我带来太多成功。

经过一些测试,似乎使用 toolbelt 的 MultipartEncoder() 时,请求将按照应该的方式将数据发送到 post 请求的 form 部分。然而,当使用 aiohttp 时,请求被放置在请求的 body 部分中。不确定它们为什么会有不同的表现。

def multipartencode() -> ClientResponse():
        # Using MultipartEncoder
        m = MultipartEncoder(
            fields={'type': type_str,
                    'metadata': json.dumps(metadata),
                    'file': (filename, file, 'application/json')}
        )

        # Using FormData
        data = FormData()
        data.add_field('file', file, filename=filename,
                       content_type='multipart/form-data')
        data.add_field('type', type_str, content_type='multipart/form-data')
        data.add_field('metadata', json.dumps(metadata),
                       content_type='multipart/form-data')

        # Using MultipartWriter
        with MultipartWriter('multipart/form-data') as mpwriter:
            part = mpwriter.append(
                file, {'CONTENT-TYPE': 'multipart/form-data'})
            part.set_content_disposition('form-data')
            part = mpwriter.append_form([('type', type_str)])
            part.set_content_disposition('form-data')
            part = mpwriter.append_form([('metadata', json.dumps(metadata))])
            part.set_content_disposition('form-data')


        # send request with ClientSession()
        resp = await session.post(url=url, data=data, headers=headers)
        return resp

我应该如何正确地格式化/构建多部分编码的请求,以便使用aiohttp发送?

1
这个问题在aiohttp上发布的可能会有帮助 https://github.com/aio-libs/aiohttp/issues/3571,特别是这个评论:https://github.com/aio-libs/aiohttp/issues/3571#issuecomment-456509079 - Ajay M
你找到解决办法了吗? - Danila Ganchar
1个回答

2
我为此苦苦挣扎了几个小时。我的特殊情况是向Mailgun发送带有附件的电子邮件。这与上面的评论链接所提到的是一样的。请查看下面的可行代码:
import asyncio

import aiohttp


async def send():
    url = "<<mailgun_url>>"
    api_key = "<<mailgun_api_key>>"

    mail_gun_data = {
        "from": "<<from>>",
        "to": "<<to>>",
        "subject": "Subject",
        "text": "Testing Mailgun",
        "attachment": open("<<file_path>>", "rb")
    }

    async with aiohttp.ClientSession() as session:
        with aiohttp.MultipartWriter("form-data") as mp:
            for key, value in mail_gun_data.items():
                part = mp.append(value)
                part.set_content_disposition('form-data', name=key)
            resp = await session.post(
                url,
                auth=aiohttp.BasicAuth("api", api_key),
                data=mp,
            )


if __name__ == '__main__':
    asyncio.run(
        send()
    )

希望这对某人有所帮助,能节省时间。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接