Skip to content
AITroveRead. Build. Understand.
Make this comfortable

Python Flask multipart uploads: bound the body, part count and accepted file

Last updated: 30 Sept 20264 min read
tutorial
IntermediateBy AITrove Editorial

A multipart request divides an HTTP body into named fields and file parts that need both parser-level and application-level limits.

Download Python source kit

Operation contract

The owned endpoint requires exactly one receipt_id field and one receipt file. Flask limits the total body, parser field memory and part count before the endpoint validates the accepted names. The application reads at most 129 file bytes for a 128-byte limit and returns only a byte count. It never uses the received filename as a storage path. Three test requests check an accepted upload, an oversized file and a multipart body containing an extra part.

Failure and ownership boundary

Parser limits do not replace reverse-proxy limits or a real transport deadline. Reaching request.form or request.files can trigger parsing, so catching errors after partially saving files is too late for staged publication. Flask error responses: preserve status without exposing internal exception text, Python JSON Lines: cap line bytes and validate the whole batch before returning it and Python pathlib files: specify encoding and close the resource owner cover the adjacent boundaries. This endpoint uses a local test client and has no public identity or storage service.

Tested environment

Dependency check: this program was executed on CPython 3.14.6 with Flask==3.1.3. Install these versions in a separate virtual environment. The download includes the recorded environment snapshot; no third-party package is part of the website runtime.

Working program

python
import io
import re
from flask import Flask, request

app = Flask(__name__)
app.config.update(MAX_CONTENT_LENGTH=4096, MAX_FORM_MEMORY_SIZE=4096, MAX_FORM_PARTS=2)
@app.errorhandler(413)
def too_large(failure):
    return {"error": "upload budget exceeded"}, 413

@app.post("/receipts")
def receive():
    if set(request.form) != {"receipt_id"} or set(request.files) != {"receipt"} or len(request.form.getlist("receipt_id")) != 1 or len(request.files.getlist("receipt")) != 1:
        return {"error": "exact parts required"}, 400
    if re.fullmatch(r"R-[0-9]{4}", request.form["receipt_id"]) is None:
        return {"error": "invalid receipt ID"}, 400
    payload = request.files["receipt"].stream.read(129)
    if len(payload) > 128:
        return {"error": "file byte budget exceeded"}, 413
    return {"bytes": len(payload)}, 201

with app.test_client() as client:
    accepted = client.post("/receipts", data={"receipt_id": "R-0041", "receipt": (io.BytesIO(b"amount=125"), "received.txt")})
    oversized = client.post("/receipts", data={"receipt_id": "R-0041", "receipt": (io.BytesIO(b"x" * 129), "received.txt")})
    extra = client.post("/receipts", data={"receipt_id": "R-0041", "receipt": (io.BytesIO(b"ok"), "received.txt"), "extra": "field"})
    print("accepted:", accepted.status_code, accepted.json)
    print("file limit:", oversized.status_code)
    print("part limit:", extra.status_code)

Output

Output
accepted: 201 {'bytes': 10}
file limit: 413
part limit: 413

Costs and limits

The endpoint retains at most 129 file bytes; parser and transport buffers have separate configured and implementation costs. A received request may already occupy upstream storage. Upload retention, authentication, content inspection and crash-durable publication are not provided by returning a count.

Common Mistakes

  • The received filename must not become an unchecked destination path.
  • Limit multipart part count as well as individual application payload size.

Connected lessons

Flask error responses: preserve status without exposing internal exception text, Python ZIP extraction: validate names and decompressed budgets before owned writes, Python JSON Lines: cap line bytes and validate the whole batch before returning it.

Follow the ownership and update boundary

Python SpooledTemporaryFile: rollover is a storage choice, not an upload limit.

python
flask-multipart-budgets
Storage details