"""Encode multipart form data to upload files via POST.""" from __future__ import print_function import mimetypes import random import string _BOUNDARY_CHARS = string . digits + string . ascii_letters def encode_multipart ( fields , files , boundary = None ): r"""Encode dict of form fields and dict of files as multipart/form-data. Return tuple of (body_string, headers_dict). Each value in files is a dict with required keys 'filename' and 'content', and optional 'mimetype' (if not specified, tries to guess mime type or uses 'application/octet-stream'). >>> body, headers = encode_multipart({'FIELD': 'VALUE'}, ... {'FILE': {'filename': 'F.TXT', 'content': 'CONTENT'}}, ... boundary='BOUNDARY') >>> print('

'.join(repr(l) for l in body.split('\r

'))) '--BOUNDARY' 'Content-Disposition: form-data; name="FIELD"' '' 'VALUE' '--BOUNDARY' 'Content-Disposition: form-data; name="FILE"; filename="F.TXT"' 'Content-Type: text/plain' '' 'CONTENT' '--BOUNDARY--' '' >>> print(sorted(headers.items())) [('Content-Length', '193'), ('Content-Type', 'multipart/form-data; boundary=BOUNDARY')] >>> len(body) 193 """ def escape_quote ( s ): return s . replace ( '"' , ' \\ "' ) if boundary is None : boundary = '' . join ( random . choice ( _BOUNDARY_CHARS ) for i in range ( 30 )) lines = [] for name , value in fields . items (): lines . extend (( '--{0}' . format ( boundary ), 'Content-Disposition: form-data; name="{0}"' . format ( escape_quote ( name )), '' , str ( value ), )) for name , value in files . items (): filename = value [ 'filename' ] if 'mimetype' in value : mimetype = value [ 'mimetype' ] else : mimetype = mimetypes . guess_type ( filename )[ 0 ] or 'application/octet-stream' lines . extend (( '--{0}' . format ( boundary ), 'Content-Disposition: form-data; name="{0}"; filename="{1}"' . format ( escape_quote ( name ), escape_quote ( filename )), 'Content-Type: {0}' . format ( mimetype ), '' , value [ 'content' ], )) lines . extend (( '--{0}--' . format ( boundary ), '' , )) body = ' \r

' . join ( lines ) headers = { 'Content-Type' : 'multipart/form-data; boundary={0}' . format ( boundary ), 'Content-Length' : str ( len ( body )), } return ( body , headers ) if __name__ == '__main__' : import doctest doctest . testmod ()