Streaming Form Data Parser
streaming-form-data is a Python library designed for parsing `multipart/form-data` HTTP requests in a streaming fashion, making it suitable for handling large file uploads and form submissions without loading the entire request body into memory. The current version is 2.0.0, and it follows an active maintenance release cadence, with major versions introducing significant changes.
Common errors
-
NameError: name 'MultipartFormParser' is not defined
cause You are using a class name from version 1.x.x of the library, which was renamed in v2.0.0.fixChange `MultipartFormParser` to `StreamingFormDataParser`. -
AttributeError: type object 'ParseFailed' has no attribute '__module__'
cause The `ParseFailed` exception was moved to a specific submodule in v2.0.0.fixUpdate the import statement: `from streaming_form_data.exceptions import ParseFailed`. -
streaming_form_data.exceptions.ParseFailed: Malformed multipart body
cause The incoming `multipart/form-data` payload is structurally incorrect, or the `Content-Type` header (especially the `boundary` parameter) does not match the actual boundary strings in the request body.fixVerify that the `Content-Type` header passed to `StreamingFormDataParser` exactly matches the client's header, particularly the `boundary`. Also, check that the payload is well-formed with correct boundary markers (`--boundary\r\n` and `--boundary--\r\n` at the end). -
TypeError: FileTarget() got an unexpected keyword argument 'filename'
cause The `FileTarget` class (previously `File`) in v2.0.0 no longer accepts a `filename` argument directly in its constructor. It expects `file_path` for a path or `target` for a file-like object.fixReplace `filename='your_file.txt'` with `file_path='/path/to/save/your_file.txt'` when instantiating `FileTarget`.
Warnings
- breaking Version 2.0.0 introduced significant breaking changes. Key classes were renamed (`MultipartFormParser` to `StreamingFormDataParser`, `File` to `FileStream`), constructor arguments for `Field` and `FileStream` were modified, and `ParseFailed` exception was moved. Python 3.10 or newer is now required.
- gotcha The `streaming_form_data.exceptions.ParseFailed` exception is raised for any malformed `multipart/form-data` payload. This includes incorrect `Content-Type` headers (missing boundary, wrong media type), or an invalid structure within the payload itself.
- gotcha By default, `streaming-form-data` assumes UTF-8 encoding for text fields. If you need robust detection for other charsets (e.g., ISO-8859-1), you must install the optional `cchardet` dependency.
- gotcha This library is designed for streaming. Failing to feed data to the parser in chunks (e.g., trying to read an entire large request body into memory before passing it to `parser.data_received`) defeats its purpose and can lead to memory exhaustion.
Install
-
pip install streaming-form-data -
pip install streaming-form-data[charset_detection]
Imports
- StreamingFormDataParser
from streaming_form_data import MultipartFormParser
from streaming_form_data import StreamingFormDataParser
- ParseFailed
from streaming_form_data import ParseFailed
from streaming_form_data.exceptions import ParseFailed
- FileTarget
from streaming_form_data.targets import File
from streaming_form_data.targets import FileTarget
- ValueTarget
from streaming_form_data.targets import ValueTarget
Quickstart
import io
from streaming_form_data import StreamingFormDataParser
from streaming_form_data.targets import ValueTarget, FileTarget
# Simulate an incoming HTTP request body and headers
boundary = b'----WebKitFormBoundary7MA4YWxkTrZu0gW'
content_type = b'multipart/form-data; boundary=' + boundary
payload = (
b'--' + boundary + b'\r\n'
b'Content-Disposition: form-data; name="text_field"\r\n'
b'\r\n'
b'Hello Streaming World!'
b'\r\n'
b'--' + boundary + b'\r\n'
b'Content-Disposition: form-data; name="file_upload"; filename="message.txt"\r\n'
b'Content-Type: text/plain\r\n'
b'\r\n'
b'This is a test file content.\nLine two.\n'
b'\r\n'
b'--' + boundary + b'--\r\n'
)
headers = {b'Content-Type': content_type}
parser = StreamingFormDataParser(headers=headers)
text_field = ValueTarget()
file_target = FileTarget(file_path='/tmp/uploaded_message.txt') # Path where the file will be saved
parser.register('text_field', text_field)
parser.register('file_upload', file_target)
# In a real web application, `request.body` would be streamed here
# For this example, we use a BytesIO object to simulate the stream
stream = io.BytesIO(payload)
while True:
chunk = stream.read(8192) # Read in chunks
if not chunk:
break
parser.data_received(chunk)
print(f"Text field value: '{text_field.value.decode()}'")
print(f"File saved to: '{file_target.file_path}'")
# Verify content (optional, for demonstration)
with open(file_target.file_path, 'rb') as f:
print(f"File content: '{f.read().decode()}'")