diff --git a/docs/backends/amazon-S3.rst b/docs/backends/amazon-S3.rst index e6aae22e3..0fb2f7b69 100644 --- a/docs/backends/amazon-S3.rst +++ b/docs/backends/amazon-S3.rst @@ -374,6 +374,71 @@ So your bucket file can be organized like as below:: | │ ├── app.js | │ ├── app.css | │ └── ... + +You could also pass different characteristics to different buckets:: + + from storages.backends.s3boto3 import S3Boto3Storage + from django.utils.deconstruct import deconstructible + + @deconstructible + class StaticStorage(S3Boto3Storage): + + bucket_name = my-app-public-bucket + location = static + + s3_static_storage = StaticStorage() + + + @deconstructible + class MediaStorage(S3Boto3Storage): + + bucket_name = my-app-public-bucket + location = media + + s3_media_storage = MediaStorage() + + + @deconstructible + class PrivStorage(S3Boto3Storage): + + custom_domain = None + signature_version = 's3v4' + bucket_name = my-app-private-bucket + location = media + + s3_priv_storage = PrivStorage() + +The above setup would use two buckets, one that could use an ``AWS_S3_CUSTOM_DOMAIN`` specified in your settings for static files in one folder and media files in another folder of the same bucket, and a separate private bucket that would ignore the custom domain setting and use signed S3 urls for private media files in a separate bucket. + +The resulting hierarchy would look like:: + + | my-app-public-bucket + | ├── media + | │ ├── public_video.mp4 + | │ ├── public_file.pdf + | │ └── ... + | ├── static + | │ ├── app.js + | │ ├── app.css + | │ └── ... + | my-app-private-bucket + | ├── media + | │ ├── private_video.mp4 + | │ ├── private_file.pdf + | │ └── ... + +The function variables in the ``custom_storage.py`` in this above example can be imported and used as storage targets in models. For instance, perhaps you have installed a media library that provides an abstract media model class, and you want the media files you upload to be private, but their thumbnail images to be in your public bucket with your other public images and static files:: + + from django.db import models + from custom_storages import s3_priv_storage, s3_media_storage + from a_media_addon.models import AbstractMedia + + class CustomMedia(AbstractMedia): + + file = models.FileField(storage=s3_priv_storage, verbose_name=_('file')) + thumbnail = models.FileField(storage=s3_media_storage, blank=True, verbose_name=_('thumbnail')) + +The above model would upload its media ``file`` to the private bucket and the ``thumbnail`` to the public bucket, in both cases using ``media`` as the folder location, as specified in the respective storage classes in ``custom_storages.py``. Model diff --git a/docs/backends/digital-ocean-spaces.rst b/docs/backends/digital-ocean-spaces.rst index c11af4bf3..66ba5dbd9 100644 --- a/docs/backends/digital-ocean-spaces.rst +++ b/docs/backends/digital-ocean-spaces.rst @@ -6,3 +6,59 @@ Digital Ocean Spaces implements the S3 protocol. To use it follow the instructio - Set ``AWS_S3_REGION_NAME`` to your Digital Ocean region (such as ``nyc3`` or ``sfo2``) - Set ``AWS_S3_ENDPOINT_URL`` to the value of ``https://${AWS_S3_REGION_NAME}.digitaloceanspaces.com`` - Set the values of ``AWS_ACCESS_KEY_ID`` and ``AWS_SECRET_ACCESS_KEY`` to the corresponding values from Digital Ocean + +Signed urls with Digital Ocean CDN domains +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +It is possible to use signed urls with a custom domain on Digital Ocean spaces. Per the `Digital Ocean docs for the Spaces API`_:: + +.. note:: + + You can use presigned URLs with the Spaces CDN. To do so, configure your SDK or S3 tool to use the non-CDN endpoint, generate a presigned URL for a GetObject request, then modify the hostname in the URL to be the CDN hostname (..cdn.digitaloceanspaces.com, unless the Space uses a custom hostname). + +To accomplish this, consider the following settings in your ``settings.py``:: + + MEDIAFILES_LOCATION = 'media' + AWS_PRIVSTORAGE_BUCKET_NAME = 'my-app-priv-bucket' + AWS_S3_CUSTOM_DOMAIN = 'cdn.mydomain.com' + AWS_S3_REGION_NAME = 'nyc3' + AWS_S3_ENDPOINT_URL = f'https://{AWS_S3_REGION_NAME}.digitaloceanspaces.com' + AWS_S3_SIGNATURE_VERSION = 's3' + +Along with the following custom storage class in ``custom_storages.py`` in the root of your Django project:: + + from django.conf import settings + from storages.backends.s3boto3 import S3Boto3Storage + from django.utils.deconstruct import deconstructible + + @deconstructible + class PrivStorage(S3Boto3Storage): + + custom_domain = None + bucket_name = settings.AWS_PRIVSTORAGE_BUCKET_NAME + location = settings.MEDIAFILES_LOCATION + + s3_priv_storage = PrivStorage() + +Digital Ocean can provide signed urls but not via a custom domain, so we have defined a custom domain but told the storage class not to use it, which will cause the url generating method to return a signed link to the object on Digital Ocean's ``digitaloceanspaces.com`` domain. Then, we could use a template tag filter or an additional method on the storage class to return the signed url, with the Digital Ocean domain replaced by the custom domain. The template tag in ``myapp/templatetags/myapp_tags.py`` might look like:: + + from django.conf import settings + + @register.filter + def cdn_url(value): + if settings.AWS_S3_ENDPOINT_URL in value: + cdn_domain = 'https://' + settings.AWS_S3_CUSTOM_DOMAIN + new_url = value.replace(settings.AWS_S3_ENDPOINT_URL, cdn_domain) + return new_url + else: + return value + +In your template(s), you could then use the filter to replace the ``digitaloceanspaces.com`` domain with your custom domain after the signed url has been generated. Assuming your page model has a ``media`` instance which represents the stored object, the above filter would be used like so:: + + {% load myapp_tags %} + + {{ self.media.url|cdn_url }} + +If your signed url contains the ``nyc3.digitaloceanspaces.com`` endpoint you specified in ``settings.py``, that hostname will be replaced by your ``AWS_S3_CUSTOM_DOMAIN``, else the media object's url value will be returned unmodified. + +.. _Digital Ocean docs for the Spaces API: https://docs.digitalocean.com/products/spaces/resources/s3-sdk-examples/#presigned-url