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

dud*_*998 5 python multipartform-data python-requests python-asyncio aiohttp

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

在尝试使用 asyncio 之前,我正在使用 requests-toolbelt MultipartEncoder( https://github.com/requests/toolbelt )同步执行该过程,这对于普通请求非常有效,但在使用 aiohttp 进行异步时不起作用。aiohttp 提供了 2 个多部分类,一个FormData()类和一个MultipartWriter()类,这两个类都没有给我带来很大的成功。

经过一些测试,似乎不同之处在于,当我使用工具带时MultipartEncoder(),请求会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
Run Code Online (Sandbox Code Playgroud)

如何正确格式化/构建多部分编码请求以使其使用 aiohttp 发送?

小智 3

我为此苦苦挣扎了几个小时。我的具体情况是将带有文件附件的电子邮件发送到 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()
    )
Run Code Online (Sandbox Code Playgroud)

我希望这会有所帮助并节省某人的时间。