2020年11月4日水曜日

Python3 の urllib を使って multipart/form-data を送信してみた

概要

urllib では multipart/form-data を送信するための便利メソッドなどは用意されていません
なので今回は自力で multipart/form-data のリクエストボディを生成してリクエストしてみました

環境

  • macOS 10.15.7
  • Python 3.8.5

テキストファイルを送信するサンプル

multipart/form-data はすごい簡単に説明すると boundary という境界文字で区切った複数のコンテンツを送信する方式です
まずは適当な文字列のフォーム+テキストファイルのフォームを送信してみました

  • vim text_file_test.py
import urllib
import urllib.request

url = "http://localhost:8080/upload"
encoding = "utf-8"
boundary = "--------python"

def multipart_formdata():
    lines = []

    lines.append('--' + boundary)
    lines.append('Content-Disposition: form-data; name="message"')
    lines.append('')
    lines.append("hello")

    lines.append('--' + boundary)
    lines.append('Content-Disposition: form-data; name="uploadfile"; filename="uploadfile.txt"')
    lines.append('Content-Type: text/plain')
    lines.append('')
    lines.append("hoge")
    lines.append('')
    lines.append("--" + boundary + "--")
    lines.append('')

    value = "\n".join(lines)
    return value.encode(encoding)

def request_with_multipart_formdata():
    req = urllib.request.Request(url)
    req.add_header("Content-Type", "multipart/form-data; boundary=%s" % boundary)
    data = multipart_formdata()
    with urllib.request.urlopen(req, data) as response:
        print(response.status)

request_with_multipart_formdata()

バイナリファイルを送信するサンプル

画像などのデータを送信する方法です
受信側が Content-Transfer-Encoding: base64 に対応していれば送信できるはずですが

import urllib
import urllib.request
import base64

url = "http://localhost:8080/upload"
encoding = "utf-8"
boundary = "--------python"

f = open("image.png", "rb", buffering=0)
file_data = f.read()
f.close()

def multipart_formdata():
    value = '--' + boundary
    value += 'Content-Disposition: form-data; name="message"'
    value += ''
    value += 'hello'
    value += '--' + boundary
    value += 'Content-Disposition: form-data; name="uploadfile"; filename="image.png"'
    value += 'Content-Type: image/png'
    value += 'Content-Transfer-Encoding: base64'
    value += ''
    value += str(base64.b64encode(file_data))
    value += ''
    value += "--" + boundary + "--"
    value += ''

    return value.encode(encoding)

def request_with_multipart_formdata():
    req = urllib.request.Request(url)
    req.add_header("Content-Type", "multipart/form-data; boundary=%s" % boundary)
    data = multipart_formdata()
    with urllib.request.urlopen(req, data) as response:
        print(response.status)

request_with_multipart_formdata()

最後に

標準の urllib ではかなり辛いので素直に requests を使うのが良いと思います

参考サイト

0 件のコメント:

コメントを投稿