I would like to POST multipart/form-data encoded data. I have found an external module that does it: http://atlee.ca/software/poster/index.html however I would rather avoid this dependency. Is there a way to do this using the standard libraries?
thanks
Multipart form data: The ENCTYPE attribute of <form> tag specifies the method of encoding for the form data. It is one of the two ways of encoding the HTML form. It is specifically used when file uploading is required in HTML form. It sends the form data to server in multiple parts because of large size of file.
To post HTML form data to the server in URL-encoded format using Python, you need to make an HTTP POST request to the server and provide the HTML form data in the body of the Python POST message. You also need to specify the data type using the Content-Type: application/x-www-form-urlencoded request header.
Multipart/form-data is ideal for sending non-ASCII or binary data, and is the only content type that allows you to upload files. For more information about form data, see http://www.w3.org/TR/html401/interact/forms.html. NOTE: In cURL, you specify each message part using the -F (or --form ) option.
The standard library does not currently support that. There is cookbook recipe that includes a fairly short piece of code that you just may want to copy, though, along with long discussions of alternatives.
It's an old thread but still a popular one, so here is my contribution using only standard modules.
The idea is the same than here but support Python 2.x and Python 3.x. It also has a body generator to avoid unnecessarily memory usage.
import codecs import mimetypes import sys import uuid try: import io except ImportError: pass # io is requiered in python3 but not available in python2 class MultipartFormdataEncoder(object): def __init__(self): self.boundary = uuid.uuid4().hex self.content_type = 'multipart/form-data; boundary={}'.format(self.boundary) @classmethod def u(cls, s): if sys.hexversion < 0x03000000 and isinstance(s, str): s = s.decode('utf-8') if sys.hexversion >= 0x03000000 and isinstance(s, bytes): s = s.decode('utf-8') return s def iter(self, fields, files): """ fields is a sequence of (name, value) elements for regular form fields. files is a sequence of (name, filename, file-type) elements for data to be uploaded as files Yield body's chunk as bytes """ encoder = codecs.getencoder('utf-8') for (key, value) in fields: key = self.u(key) yield encoder('--{}\r\n'.format(self.boundary)) yield encoder(self.u('Content-Disposition: form-data; name="{}"\r\n').format(key)) yield encoder('\r\n') if isinstance(value, int) or isinstance(value, float): value = str(value) yield encoder(self.u(value)) yield encoder('\r\n') for (key, filename, fd) in files: key = self.u(key) filename = self.u(filename) yield encoder('--{}\r\n'.format(self.boundary)) yield encoder(self.u('Content-Disposition: form-data; name="{}"; filename="{}"\r\n').format(key, filename)) yield encoder('Content-Type: {}\r\n'.format(mimetypes.guess_type(filename)[0] or 'application/octet-stream')) yield encoder('\r\n') with fd: buff = fd.read() yield (buff, len(buff)) yield encoder('\r\n') yield encoder('--{}--\r\n'.format(self.boundary)) def encode(self, fields, files): body = io.BytesIO() for chunk, chunk_len in self.iter(fields, files): body.write(chunk) return self.content_type, body.getvalue()
Demo
# some utf8 key/value pairs fields = [('প্রায়', 42), ('bar', b'23'), ('foo', 'ން:')] files = [('myfile', 'image.jpg', open('image.jpg', 'rb'))] # iterate and write chunk in a socket content_type, body = MultipartFormdataEncoder().encode(fields, files)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With