How Can FastAPI Make Your File Uploads Lightning Fast?

Mastering File Uploads with FastAPI: A Seamless Dance of Code and Bytes

How Can FastAPI Make Your File Uploads Lightning Fast?

Creating endpoints for file uploads is a pretty common task when you’re putting together web applications. FastAPI, the slick web framework for Python, makes the whole process a breeze. Let’s walk through how to set up multi-part file upload endpoints using FastAPI, along with a sprinkle of Pydantic models for that extra validation goodness.

First things first, setting up your environment is a must. You’ll need Python 3.7 or higher and the FastAPI and Uvicorn packages. You get all of this with a quick pip install:

pip install fastapi uvicorn python-multipart

That python-multipart package? It’s your best buddy for handling HTTP multipart requests, which is how files are sent through HTTP.

Let’s cook up a basic FastAPI app to lay down some foundations. Here’s how you get started:

from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse

app = FastAPI()

@app.post("/files/")
async def create_files(files: list[bytes] = File(description="Multiple files as bytes")):
    return {"file_sizes": [len(file) for file in files]}

@app.post("/uploadfiles/")
async def create_upload_files(files: list[UploadFile]):
    return {"filenames": [file.filename for file in files]}

@app.get("/")
async def main():
    content = """
    <body>
        <form action="/files/" enctype="multipart/form-data" method="post">
            <input name="files" type="file" multiple>
            <input type="submit">
        </form>
        <form action="/uploadfiles/" enctype="multipart/form-data" method="post">
            <input name="files" type="file" multiple>
            <input type="submit">
        </form>
    </body>
    """
    return HTMLResponse(content=content)

The app has two endpoints for file uploads: one handling files as raw bytes and the other using the UploadFile class. UploadFile gives you more details about the file, like its name and type, which can come in handy.

When it comes to handling file uploads, you have two main options in FastAPI: File and UploadFile. Both have their uses. File works if you just need the file content in bytes, but UploadFile is the winner if you need more info or flexibility.

To handle multiple files, you can just work with lists:

from fastapi import FastAPI, File, UploadFile

app = FastAPI()

@app.post("/files/")
async def create_files(files: list[bytes] = File(description="Multiple files as bytes")):
    return {"file_sizes": [len(file) for file in files]}

@app.post("/uploadfiles/")
async def create_upload_files(files: list[UploadFile]):
    return {"filenames": [file.filename for file in files]}

Need to save the uploaded files to your server? Here’s how:

from fastapi import FastAPI, UploadFile

app = FastAPI()

@app.post("/upload/")
async def upload_file(file: UploadFile):
    try:
        file_path = f"C:\\Users\\hp\\OneDrive\\Documents\\gfg/{file.filename}"
        with open(file_path, "wb") as f:
            f.write(file.file.read())
        return {"message": "File saved successfully"}
    except Exception as e:
        return {"message": str(e)}

This snippet saves the uploaded files in a specific directory. Make sure you’ve got your file paths sorted to avoid creating a mess.

Now, let’s sprinkle in some Pydantic models for validation. These models make sure the data coming into your endpoints is structured and valid:

from pydantic import BaseModel
from fastapi import FastAPI, File, UploadFile

app = FastAPI()

class FileMetadata(BaseModel):
    filename: str
    content_type: str

@app.post("/upload/")
async def upload_file(file: UploadFile):
    metadata = FileMetadata(filename=file.filename, content_type=file.content_type)
    try:
        file_path = f"C:\\Users\\hp\\OneDrive\\Documents\\gfg/{file.filename}"
        with open(file_path, "wb") as f:
            f.write(file.file.read())
        return {"message": "File saved successfully", "metadata": metadata.dict()}
    except Exception as e:
        return {"message": str(e)}

The FileMetadata model is used here to structure the metadata of the uploaded file. Pydantic models are like magic when it comes to validation and ensuring your data stays in check.

Want to handle files along with additional metadata? Here’s a neat way to do it:

from pydantic import BaseModel
from fastapi import FastAPI, File, UploadFile

app = FastAPI()

class ProfileUpdateSchema(BaseModel):
    email: str
    picture: UploadFile

@app.post("/upload/")
async def update_profile(payload: ProfileUpdateSchema):
    try:
        file_path = f"C:\\Users\\hp\\OneDrive\\Documents\\gfg/{payload.picture.filename}"
        with open(file_path, "wb") as f:
            f.write(payload.picture.file.read())
        return {"message": "Profile updated successfully", "email": payload.email}
    except Exception as e:
        return {"message": str(e)}

In this setup, the ProfileUpdateSchema model lets you upload a file alongside additional data, like an email.

A few best practices for handling file uploads:

  • Use UploadFile: It’s generally better unless you have a specific reason to use File.
  • Validate File Types: Always check the file types to avoid unauthorized uploads.
  • Handle Large Files: Streaming large files can help prevent memory issues.
  • Async/Await: Since FastAPI is built on asynchronous libraries, always use async/await to keep things smooth.

FastAPI makes handling file uploads straightforward and efficient. With UploadFile and Pydantic models, you’re armed with the tools to create solid, validated endpoints for file uploads. Stick to best practices, and your application will be both secure and efficient. Happy coding!