How to Parse Multipart Form Data in Python
Parse multipart/form-data uploads using the Python standard library's cgi module to extract both regular fields and file uploads.
Python code
63 linesimport cgi
from io import BytesIO
def parse_multipart_form(headers, body_bytes):
content_type = headers.get("Content-Type", "")
content_length = int(headers.get("Content-Length", len(body_bytes)))
# Create a file-like object from bytes for cgi.FieldStorage
body_file = BytesIO(body_bytes)
# Parse the multipart form data
form = cgi.FieldStorage(
fp=body_file,
headers=headers,
environ={
"REQUEST_METHOD": "POST",
"CONTENT_TYPE": content_type,
"CONTENT_LENGTH": content_length,
}
)
result = {}
for field in form.list:
if field.filename:
# File upload
result[field.name] = {
"filename": field.filename,
"content_type": field.type,
"data": field.file.read() or b""
}
else:
# Regular field
result[field.name] = field.value
return result
if __name__ == "__main__":
# Simulate a multipart form upload
boundary = "----WebKitFormBoundary7MA4YWxkTrZu0gW"
# Build a simple multipart body
body = (
f"--{boundary}\r\n"
f'Content-Disposition: form-data; name="username"\r\n\r\n'
f"alice\r\n"
f"--{boundary}\r\n"
f'Content-Disposition: form-data; name="file"; filename="test.txt"\r\n'
f"Content-Type: text/plain\r\n\r\n"
f"Hello, World!\r\n"
f"--{boundary}--\r\n"
).encode()
headers = {
"Content-Type": f"multipart/form-data; boundary={boundary}",
"Content-Length": str(len(body)),
}
# Parse it
parsed = parse_multipart_form(headers, body)
# Show results
import pprint
pprint.pprint(parsed, width=80)
Output
{'file': {'content_type': 'text/plain', 'data': b'Hello, World!', 'filename': 'test.txt'}, 'username': 'alice'}
How it works
The cgi.FieldStorage class parses multipart uploads when given the raw request body and headers. It needs an environ dict that mimics WSGI/CGI variables like REQUEST_METHOD, CONTENT_TYPE, and CONTENT_LENGTH. The form.list attribute exposes each parsed field, where file uploads have a filename property (indicating file data) and regular fields have a simple value. File data is read from the field's file object, which is a BytesIO-like stream. This approach uses only the standard library, making it dependency-free for prototyping or legacy environments.
Common mistakes
- Using `json.loads` instead of `cgi.FieldStorage` for multipart data
- Forgetting to pass `CONTENT_LENGTH` in the environ dict, causing empty or partial parses
- Not accounting for missing `filename` when checking if a field is a file upload
- Ignoring that `cgi` is deprecated in Python 3.11+, consider alternatives for new projects
Variations
- Use the `python-multipart` library with `multipart.parse_form` for better performance and deprecation safety
- In a web framework, use `request.form` and `request.files` (Flask) or `request.body` with a parser (FastAPI)
Real-world use cases
- Building a minimal HTTP server handler that accepts file uploads without extra dependencies
- Inside automation scripts and CLIs that need to how to parse multipart form data in python as one step of a larger job.
- In data-cleaning or ETL pipelines where you how to parse multipart form data in python before validating or storing records.
Sponsored
More from API design & gRPC
- Build a Bulk Array POST Mock Server in Python medium
- Build a Mock REST API with PUT and GET in Python medium
- Convert Protobuf to JSON and Dict in Python easy
- Create a Data Helper in Python for gRPC-style APIs easy
- Format data in Python using dataclasses like gRPC messages easy
- Generate an OpenAPI Spec from Mock Routes in Python easy
Keep learning
Related tutorials and quizzes for this topic.