Wrote this. Turns out I don't need it at all since urllib3 has the exact same function. welp.
Created
November 16, 2020 14:39
-
-
Save takase1121/64b09df8336a9ca23aa1db3cb62de016 to your computer and use it in GitHub Desktop.
Generate multipart data in Python3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import unittest | |
from os import urandom | |
from io import BytesIO | |
from binascii import b2a_base64, hexlify | |
def is_bytes_like(input): | |
"""Returns true of input is a byte-like object""" | |
return hasattr(input, "decode") | |
def create_multipart(multi): | |
""" | |
Creates a multipart form data and its MIME type | |
data is a dictionary {name: (data, type, filename)} | |
name is written to the name field on Content-Disposition subheader | |
data is data as byte-like object or string. Byte-like object is encoded to base64 while strings are encoded as utf8. | |
type is Content-Type of the data | |
filename is written to the filename field on Content-Disposition subheader | |
All fields in tuple are optional except data. However, they must be filled with a falsy value such as None | |
returns (mime_type, multipart_data) | |
mime_type is a string you use for request's Content-Type header | |
multipart_data is a byte-like object that contains the multipart data | |
""" | |
boundary = hexlify(urandom(16)) | |
output = BytesIO() | |
for key, (data, type, filename) in multi.items(): | |
output.write(b"--") | |
output.write(boundary) | |
output.write(b"\r\n") | |
output.write(b'Content-Disposition:form-data;name="') | |
output.write(key.encode("ascii")) | |
output.write(b'";') | |
if filename: | |
output.write(b'filename="') | |
output.write(filename.encode("ascii")) | |
output.write(b'"') | |
output.write(b"\r\n") | |
if type: | |
output.write(b"Content-Type:") | |
output.write(type.encode("ascii")) | |
output.write(b"\r\n") | |
output.write( | |
b2a_base64(data, newline=False) | |
if is_bytes_like(data) | |
else data.encode("utf-8") | |
) | |
output.write(b"\r\n") | |
output.write(b"--") | |
output.write(boundary) | |
output.write(b"--") | |
content_type = b"Content-Type:multipart/form-data;boundary=" + boundary | |
return content_type.decode("ascii"), output.getvalue() | |
class MultipartTest(unittest.TestCase): | |
"""A little unit test for multipart data""" | |
def test_general(self): | |
mime, output = create_multipart( | |
{ | |
"name": ("John Doe", None, None), | |
"result": ("Lorem ipsum...", "text/plain", None), | |
"attachment": (b"FILE", None, "file.txt"), | |
} | |
) | |
# get boundary | |
import re | |
boundary = re.search(r"boundary=([0-9a-f]+)$", mime)[1] | |
boundary = "--" + boundary | |
self.maxDiff = None | |
self.assertMultiLineEqual( | |
output.decode("utf-8"), | |
( | |
boundary | |
+ "\r\n" | |
+ 'Content-Disposition:form-data;name="name";\r\n' | |
+ "John Doe" | |
+ "\r\n" | |
+ boundary | |
+ "\r\n" | |
+ 'Content-Disposition:form-data;name="result";\r\n' | |
+ "Content-Type:text/plain\r\n" | |
+ "Lorem ipsum..." | |
+ "\r\n" | |
+ boundary | |
+ "\r\n" | |
+ 'Content-Disposition:form-data;name="attachment";filename="file.txt"\r\n' | |
+ "RklMRQ==" | |
+ "\r\n" | |
+ boundary | |
+ "--" | |
), | |
) | |
if __name__ == "__main__": | |
unittest.main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment