multipart/form-data

Django を使ってファイルをアップロードする時は
enctype="multipart/form-data" が必須ですが、
じゃあ Python だけの場合、どうやったら良いのだろう?
ということで調べてみました。
英語エントリしか発見できなかったので、記録程度に書いておきます。


アップロードするファイルは同名で複数を想定しています。
ソースが一部泥臭かったりしますが、ご容赦ください…。


# -*- coding: utf-8 -*-
import json
import httplib2
import mimetypes
import urllib

BOUNDARY = u'----------ThIs_Is_tHe_bouNdaRY_$'
CRLF = '\r\n'

def get_by_multipart_form_data(url, param, files):
     """
     multipart/form-data 形式で指定 URL にアクセス
     """
     body_list = []
     body_list = _set_value_to_body(body_list, "param", param)
     body_list = _set_files_to_body(body_list, "files", files)
     body_list.append('--' + BOUNDARY + '--')
     body_list.append('')
     body = CRLF.join(map(str, body_list))
     content_type = 'multipart/form-data; boundary=%s' % BOUNDARY
     client = httplib2.Http()
     headers = {'Content-Type': content_type, 'Content-Length': str(len(body))}
     res_header, res_body = client.request(url, 'POST', headers=headers, body=body)
     return res_header, res_body

def _set_value_to_body(list, key, value):
     list.append("--" + BOUNDARY)
     list.append('Content-Disposition: form-data; name="%s"' % key)
     list.append('')
     list.append(value)
     return list

def _set_files_to_body(list, key, files):
     for file in files:
          list.append('--' + BOUNDARY)
          list.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, file.name))
          list.append('Content-Type: %s' % _get_content_type(file.name))
          list.append('')
     for chunk in file.chunks():
          list.append(chunk)
     return list

def _get_content_type(filename):
     return mimetypes.guess_type(filename)[0] or 'application/octet-stream'

ご参考になりましたら幸いです。