Skip to content

Upload Files

Uploading files from your application is something extremely common.

Esmerald handles this by using the UploadFile datastructure object, serving as interface. To make this work you will need to type your uploads accordingly.

UploadFile

To access the UploadFile datastructrure.

from esmerald.datastructures import UploadFile

or

from esmerald import UploadFile

Content Types

The body parameter plays a great role when it comes to use the UploadFile. The reason for that is because of the parameter media_type.

The media_type allows to pass different encoding types for your uploads.

Esmerald EncodingType can be accessed via:

from esmerald.enums import EncodingType

The current available content-types available in the EncodingType are:

  • application/x-www-form-urlencoded
  • multipart/form-data
  • application/json

These types are the ones that shall be passed to Body(media_type=...).

Version

From the version 0.8+, Esmerald supports separate types to handle forms and file uploads independently but they will always derive from the Body parameter, which means, it is optional to use File and Form as the same result can be achieved by using Body(media_type=...).

Single file upload

Uploading a single file, you need to type the data as UploadFile.

from esmerald import Body, Esmerald, Gateway, JSONResponse, UploadFile, post
from esmerald.enums import EncodingType, MediaType


@post("/upload")
async def upload_file(
    data: UploadFile = Body(media_type=EncodingType.MULTI_PART),
) -> JSONResponse:
    """
    Uploads a file into the system
    """
    content = await data.read()
    name = data.filename
    return JSONResponse({"filename": name, "content": content.decode()})


app = Esmerald(routes=[Gateway(handler=upload_file, name="upload-file")])

List files upload

In a similar way, you can also access the files as a list.

from typing import Dict, List

from esmerald import Body, Esmerald, Gateway, UploadFile, post
from esmerald.enums import EncodingType, MediaType


@post("/upload")
async def upload_file(
    data: List[UploadFile] = Body(media_type=EncodingType.MULTI_PART),
) -> Dict[str, str]:
    """
    Uploads a file into the system
    """
    contents = {}
    for file in data:
        content = await file.read()
        contents[file.filename] = content.decode()

    return contents


app = Esmerald(routes=[Gateway(handler=upload_file, name="upload-file")])

Using the File

As mentioned above, File is now fully supported as parameter as well and since File inherits from Body, means all the benefits are also there but is also cleaner as you don't need to specify the media_type=EncodingType.MULTI_PART because it is already the default of File.

Let us see how it looks now using File instead of Body for the uploads.

Single file upload

Uploading a single file, you need to type the data as UploadFile.

from esmerald import Esmerald, File, Gateway, JSONResponse, UploadFile, post


@post("/upload")
async def upload_file(
    data: UploadFile = File(),
) -> JSONResponse:
    """
    Uploads a file into the system
    """
    content = await data.read()
    name = data.filename
    return JSONResponse({"filename": name, "content": content.decode()})


app = Esmerald(routes=[Gateway(handler=upload_file, name="upload-file")])

List files upload

In a similar way, you can also access the files as a list.

from typing import Dict, List

from esmerald import Esmerald, File, Gateway, UploadFile, post


@post("/upload")
async def upload_file(
    data: List[UploadFile] = File(),
) -> Dict[str, str]:
    """
    Uploads a file into the system
    """
    contents = {}
    for file in data:
        content = await file.read()
        contents[file.filename] = content.decode()

    return contents


app = Esmerald(routes=[Gateway(handler=upload_file, name="upload-file")])

Important

Since Body or Field are Pydantic fields (sort of), that also means you can specify for instance, the maximum number of items to be sent in a list for the upload.

from typing import Dict, List

from esmerald import Esmerald, File, Gateway, UploadFile, post


@post("/upload")
async def upload_file(
    data: List[UploadFile] = File(max_length=3),
) -> Dict[str, str]:
    """
    Uploads a file into the system
    """
    contents = {}
    for file in data:
        content = await file.read()
        contents[file.filename] = content.decode()

    return contents


app = Esmerald(routes=[Gateway(handler=upload_file, name="upload-file")])

This means that the maximum number of files allowed for upload using the a list is three. If the maximum is exceeded, it will raise a ValidationErrorException specifying the issues.

Using the max_length is just one attribute available inside File or Body. Since it is derived from pydantic FieldInfo, that means you can also specify other parameters as well.

You can check the list of available parameters default as well.

File from params

The file used with the example as param is not a datastructure but a FieldInfo so it cannot be used to set and create a new file like the one from File in datastructures.

To import it:

from esmerald.datastructures import File # datastructure

# or

from esmerald.params import File # param

API Reference

Read more about the UploadFile in the API Reference