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.

Medium Python 3.9+ Aug 9, 2026 API design & gRPC 13 views 0 copies

Python code

63 lines
Python 3.9+
import 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

stdout
{'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

  1. Use the `python-multipart` library with `multipart.parse_form` for better performance and deprecation safety
  2. 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

Run this sample

Open the browser IDE to tweak the example and see results without installing anything.

Open editor

More from API design & gRPC

Related tutorials and quizzes for this topic.