Skip to main content

Assets Management

Backend reference

Files storage

By default, backend is configured to use S3 as its files storage. DEFAULT_FILE_STORAGE variable in settings.py file is set to common.storages.PrivateS3Boto3StorageWithCDN. This is S3Boto3Storage class that comes by default with django-storages library, extended with support to CloudFront distribution as CDN and query string signature to allow access files in private S3 Bucket. There is also second storage available - common.storages.PublicS3Boto3StorageWithCDN. This one is intended for use with files that don't have to be protected with query string signature, like for example users' avatars. Using this storage, files are uploaded with public-read acl set. Those files are cached and provided by CDN - everyone who has url can access them.

Both S3 Bucket and CloudFront Distribution are created as a part of the Components Stack.

Uploading files

graphene and graphene-django do not support file uploads out of the box. Support for this functionality is added with graphene-file-upload library. It follows the specification available here.

If you don't have control over file names (e.g., files are uploaded by users) and want to avoid filename collisions (so that one user doesn't overwrite other user's files) you can use common.storages.UniqueFilePathGenerator class:

from common.storages import UniqueFilePathGenerator

class Document(models.Model):
file = models.FileField(upload_to=UniqueFilePathGenerator("documents"))

It accepts path prefix, and appends randomly generated hash to the path before the file is stored on S3. If name uniqueness is assured other way, you could simply use upload_to="documents" there.

Fetching file data with GraphQL

FileField type is converted to ObjectType for GraphQL, so that when user fetches for the object having field with FileField type, he can fetch for its details like:

file {
name
url
}

Currently name and url are only available parameters, but it can be easily extended in FileFieldType class that can be found in common/graphql/field_conversions.py, so that if needed, you can easily add fields like size, extension and more.

Creating image thumbnail

Backend provides mixin that can be used, when we need to automatically generate thumbnails for uploaded images. Example usage can be found in UserAvatar model:

from common.models import ImageWithThumbnailMixin

class UserAvatar(ImageWithThumbnailMixin, models.Model):
original = models.ImageField(
storage=PublicS3Boto3StorageWithCDN, upload_to=UniqueFilePathGenerator("avatars"), null=True
)
thumbnail = models.ImageField(
storage=PublicS3Boto3StorageWithCDN, upload_to=UniqueFilePathGenerator("avatars/thumbnails"), null=True
)

THUMBNAIL_SIZE = (128, 128)

Automatic thumbnail creation currently works for JPEG, PNG and GIF files. If other formats are to be accepted, you need to extend the FILE_FORMATS dict from ImageWithThumbnailMixin class declaration. By default, image is not being saved at all if format is invalid (is not whitelisted).

Webapp reference

Webapp is configured with apollo-upload-client package that handles requests with files. To upload file simply pass it to the Apollo's mutator file in payload object:

const [commitFileMutation] = useMutation(
gql(`
...
node {
file {
name
url
}
}
`
);

const handleUpload = (file: File) => {
commitFileMutation({
file
})
}