プログラムを中心とした個人的なメモ用のブログです。 タイトルは迷走中。
内容の保証はできませんのであしからずご了承ください。

2022/06/29

[Python] aiohttp で multipart/form-data を使う

event_note2022/06/28 23:15

複数のデータを送るときに使う multipart/form-data を aiohttp で送る方法です。

公式ドキュメントは以下です。

基本サンプル

import asyncio
import aiohttp

async def main():
    with aiohttp.MultipartWriter("form-data") as mpwriter:
        mpwriter.append('hello1')
        mpwriter.append('hello2')
        async with aiohttp.ClientSession() as session:
            res = await session.post(f'url', data=mpwriter)
            print(res)

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

以下のような内容で送信されました。

Content-Type: multipart/form-data; boundary=69ae25abec5c4a038dc2deb4a0952eb3

--69ae25abec5c4a038dc2deb4a0952eb3
Content-Type: text/plain; charset=utf-8
Content-Length: 6

hello1
--69ae25abec5c4a038dc2deb4a0952eb3
Content-Type: text/plain; charset=utf-8
Content-Length: 6

hello2
--69ae25abec5c4a038dc2deb4a0952eb3--

テキストデータ

mpwriter.append にテキストを渡した場合、デフォルトでは Content-Typetext/plain になるそうです。

バイナリデータ

mpwriter.append にバイナリデータを渡した場合、デフォルトでは Content-Typeapplication/octet-stream になるそうです。
変更したい場合は以下のように第2引数で指定します。

mpwriter.append(image, {'Content-Type': 'image/jpeg'})

また、namefilename などを指定したい場合は、set_content_disposition を使用するようです。

part = mpwriter.append(image, {'Content-Type': 'image/jpeg'})
part.set_content_disposition('form-data', name="name", filename="filename")

JSON を送りたい場合

append_json に dict 型のデータを渡せばOKです。

mpwriter.append_json({'test': 'passed'})

JSON と画像を一緒に送る例

以前、requests モジュールを使って multipart/form-data のデータを送る記事を書きました。

これを aiohttp で書いてみたのが以下です。

import asyncio
import aiohttp
from datetime import datetime

async def main():
    with aiohttp.MultipartWriter("form-data") as mpwriter:
        for camera_id in ['hoge', 'piyo']: # 各カメラのID
            # 適当なテキストデータ
            json_data = {
                'camera_id': camera_id,
                'datetime': datetime.now().isoformat()
            }
            # 適当な画像ファイルを読み込む
            image = None
            filename = 'send_image.jpg'
            with open(filename, 'rb') as f:
                image = f.read()

            # JSON の追加
            part = mpwriter.append_json(json_data)
            part.set_content_disposition('form-data', name=camera_id)

            # 画像の追加
            part = mpwriter.append(image, {'Content-Type': 'image/jpeg'})
            part.set_content_disposition('form-data', name=camera_id, filename=camera_id)

        # 送信
        async with aiohttp.ClientSession() as session:
            res = await session.post(f'http://localhost:8000', data=mpwriter)
            print(res)

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