Files
python-doc/Docs/Libs/FastAPI/14-File-Post.md
2026-05-13 17:15:18 +03:30

8.9 KiB
Raw Blame History

FastAPI POST Requests with File Uploads

Overview

This document explains how to handle file uploads in FastAPI using POST requests.

File upload endpoints are commonly used when an API needs to receive:

Images
Documents
PDF files
Text files
Binary files
Multiple files in one request

FastAPI supports file uploads using:

File
UploadFile

For real applications, UploadFile is usually preferred because it provides file metadata and handles large files more efficiently.


1. Required Package

FastAPI file uploads require python-multipart.

Install it with:

pip install python-multipart

If you are using the standard FastAPI installation, it may already be included:

pip install "fastapi[standard]"

2. Example Application

Create or update main.py with the following content:

from fastapi import FastAPI, File, UploadFile
from typing import List

app = FastAPI()


@app.post("/file")
def upload_file_bytes(file: bytes = File(...)):
    """
    Receive a file as raw bytes.
    Returns the size of the uploaded file.
    """
    return {
        "file_size": len(file)
    }


@app.post("/uploadfile")
async def upload_file_uploadfile(file: UploadFile):
    """
    Receive a file as an UploadFile object.
    Returns filename, content type, and file size.
    """
    content = await file.read()

    return {
        "filename": file.filename,
        "content_type": file.content_type,
        "file_size": len(content)
    }


@app.post("/uploadmultifile")
async def upload_multiple_files(files: List[UploadFile]):
    """
    Receive multiple files as UploadFile objects.
    Returns filenames and content types.
    """
    result = []

    for file in files:
        result.append({
            "filename": file.filename,
            "content_type": file.content_type
        })

    return result

3. File Upload as Bytes

Endpoint

@app.post("/file")
def upload_file_bytes(file: bytes = File(...)):
    return {
        "file_size": len(file)
    }

This endpoint receives the uploaded file as raw bytes.

Endpoint URL

POST /file

Example Request

curl -X POST "http://localhost:8000/file" \
  -F "file=@example.txt"

Example Response

{
  "file_size": 128
}

Explanation

file: bytes = File(...)

This tells FastAPI to expect a file field named file.

The uploaded file is loaded directly into memory as bytes.

When to Use This Method

This method is suitable for:

Small files
Simple testing
Direct in-memory processing
Quick file size checks

Limitation

This method does not provide file metadata such as:

Original filename
Content type
File headers

It also loads the whole file into memory, so it is not ideal for large files.


4. File Upload with UploadFile

Endpoint

@app.post("/uploadfile")
async def upload_file_uploadfile(file: UploadFile):
    content = await file.read()

    return {
        "filename": file.filename,
        "content_type": file.content_type,
        "file_size": len(content)
    }

Endpoint URL

POST /uploadfile

Example Request

curl -X POST "http://localhost:8000/uploadfile" \
  -F "file=@example.txt"

Example Response

{
  "filename": "example.txt",
  "content_type": "text/plain",
  "file_size": 128
}

5. Why Use UploadFile

UploadFile is better than raw bytes for most real APIs.

It provides useful metadata:

file.filename
file.content_type
file.file

It also supports file operations such as:

await file.read()
await file.write()
await file.seek()
await file.close()

Important Note

When using UploadFile.read(), the endpoint should usually be asynchronous:

async def upload_file_uploadfile(file: UploadFile):
    content = await file.read()

This is better than writing:

def upload_file_uploadfile(file: UploadFile):
    content = file.read()

because file.read() is asynchronous and should be awaited.


6. Multiple File Uploads

Endpoint

@app.post("/uploadmultifile")
async def upload_multiple_files(files: List[UploadFile]):
    result = []

    for file in files:
        result.append({
            "filename": file.filename,
            "content_type": file.content_type
        })

    return result

Endpoint URL

POST /uploadmultifile

Example Request

curl -X POST "http://localhost:8000/uploadmultifile" \
  -F "files=@file1.txt" \
  -F "files=@file2.txt"

Example Response

[
  {
    "filename": "file1.txt",
    "content_type": "text/plain"
  },
  {
    "filename": "file2.txt",
    "content_type": "text/plain"
  }
]

Explanation

files: List[UploadFile]

This tells FastAPI to receive multiple uploaded files using the same form field name:

files

Each uploaded file is handled as an UploadFile object.


7. Content-Type for File Uploads

File uploads use:

Content-Type: multipart/form-data

When using curl with -F, this header is generated automatically.

Example:

curl -X POST "http://localhost:8000/uploadfile" \
  -F "file=@example.txt"

The request sends the file as a multipart form field.


8. Complete Recommended Version

from fastapi import FastAPI, File, UploadFile, HTTPException, status
from typing import List

app = FastAPI()

MAX_FILE_SIZE = 5 * 1024 * 1024


@app.get("/")
def root():
    return {
        "message": "API is working"
    }


@app.post("/file")
def upload_file_bytes(file: bytes = File(...)):
    if len(file) > MAX_FILE_SIZE:
        raise HTTPException(
            status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
            detail="File is too large"
        )

    return {
        "file_size": len(file)
    }


@app.post("/uploadfile")
async def upload_file_uploadfile(file: UploadFile):
    content = await file.read()

    if len(content) > MAX_FILE_SIZE:
        raise HTTPException(
            status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
            detail="File is too large"
        )

    return {
        "filename": file.filename,
        "content_type": file.content_type,
        "file_size": len(content)
    }


@app.post("/uploadmultifile")
async def upload_multiple_files(files: List[UploadFile]):
    result = []

    for file in files:
        content = await file.read()

        if len(content) > MAX_FILE_SIZE:
            raise HTTPException(
                status_code=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE,
                detail=f"File is too large: {file.filename}"
            )

        result.append({
            "filename": file.filename,
            "content_type": file.content_type,
            "file_size": len(content)
        })

    return result

9. Running the Application

Start the FastAPI service using uvicorn:

uvicorn main:app --reload

The application will be available at:

http://localhost:8000

Interactive API documentation:

http://localhost:8000/docs

Alternative API documentation:

http://localhost:8000/redoc

10. Testing with curl

Upload File as Bytes

curl -X POST "http://localhost:8000/file" \
  -F "file=@example.txt"

Upload Single File with UploadFile

curl -X POST "http://localhost:8000/uploadfile" \
  -F "file=@example.txt"

Upload Multiple Files

curl -X POST "http://localhost:8000/uploadmultifile" \
  -F "files=@file1.txt" \
  -F "files=@file2.txt"

11. bytes vs UploadFile

Feature bytes UploadFile
File content Loaded directly into memory Uses file-like object
Filename Not available Available
Content type Not available Available
Best for Small files Large files and real APIs
Metadata No Yes
Recommended for production Usually no Yes

12. Best Practices

Use UploadFile for real-world APIs.

Use bytes only for small files or simple testing.

Validate file size on the server.

Validate file type before processing or storing the file.

Do not trust the uploaded filename.

Do not store uploaded files directly using user-provided names.

Avoid loading very large files fully into memory.

Use HTTPS for secure file transfer.

Store files in dedicated storage such as:

Local disk
Object storage such as S3 or MinIO
Database storage when appropriate
Network file storage

Return clear metadata to the client, such as:

Filename
Content type
File size
Upload status