Upload File to Space Digitalocean With Python
DigitalOcean Spaces is an S3-uniform object storage service with a built-in Content Commitment Network (CDN). Like Amazon S3, it provides a uncomplicated, cost-effective way to shop static files.
This tutorial shows how to configure Django to load and serve up static and user uploaded media files, public and individual, via DigitalOcean Spaces.
Prefer to use S3? Check out Storing Django Static and Media Files on Amazon S3.
- DigitalOcean Spaces
- Django Project
- Django Storages
- Static Files
- Public Media Files
- Individual Media Files
- Determination
DigitalOcean Spaces
DigitalOcean Spaces vs AWS S3?
As mentioned, DigitalOcean Spaces is an object storage service. When compared to S3, DigitalOcean Spaces:
- is easier to apply
- has a simpler and more predicable pricing model
- has much better documentation
That said, DigitalOcean Spaces is less flexible than S3 and has much less regions compared.
DigitalOcean Spaces is much easier to set upward and use than S3. Adding on a CDN requires a few button clicks from the UI. Plus, it'due south just as developer-friendly as S3. If you're already using DigitalOcean, you should definitely utilize it.
Getting Started
Showtime, y'all'll need to sign up for a DigitalOcean account (if you don't already accept ane), and then generate Spaces admission keys so you can access the DigitalOcean Spaces API.
Go alee and create the keys. Yous should take an access key ID and a hush-hush access key. With them, you lot can interact with the Spaces API with Boto3.
Case:
import boto3 # configure session and client session = boto3 . session . Session () client = session . customer ( 's3' , region_name = 'nyc3' , endpoint_url = 'https://nyc3.digitaloceanspaces.com' , aws_access_key_id = 'YOUR_ACCESS_KEY_ID' , aws_secret_access_key = 'YOUR_SECRET_ACCESS_KEY' , ) # create new bucket client . create_bucket ( Bucket = 'your-bucket-name' ) # upload file with open up ( 'exam.txt' , 'rb' ) as file_contents : client . put_object ( Bucket = 'your-bucket-proper noun' , Key = 'test.txt' , Body = file_contents , ) # download file client . download_file ( Bucket = 'your-bucket-name' , Fundamental = 'test.txt' , Filename = 'tmp/test.txt' , )
For more examples, cheque out Using DigitalOcean Spaces with AWS S3 SDKs.
Side by side, from the DigitalOcean control panel, click "Create" in the top right, and and so click "Spaces" in the dropdown. Select a region, enable a CDN, select "Restrict File List", and create a unique name for your saucepan. Then, create the saucepan.
You should encounter something like to:
Django Project
Clone down the base project from the django-digitalocean-spaces repo on GitHub:
$ git clone -b base https://github.com/testdrivenio/django-digitalocean-spaces $ cd django-digitalocean-spaces
From the project root, create the images and spin up the Docker containers:
$ docker-etch upwards -d --build
Once the build is complete, collect the static files:
$ docker-compose exec spider web python manage.py collectstatic
And then, navigate to http://localhost:1337:
You should be able to upload an image, and and so view the image at http://localhost:1337/media/IMAGE_FILE_NAME.
The radio buttons, for public vs. private, exercise not piece of work. We'll be adding this functionality later in this tutorial. Ignore them for now.
Take a quick await at the projection structure before moving on:
├── .gitignore ├── LICENSE ├── README.md ├── app │ ├── Dockerfile │ ├── hello_django │ │ ├── __init__.py │ │ ├── asgi.py │ │ ├── settings.py │ │ ├── urls.py │ │ └── wsgi.py │ ├── manage.py │ ├── mediafiles │ ├── requirements.txt │ ├── static │ │ └── bulma.min.css │ ├── staticfiles │ └── upload │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── templates │ │ └── upload.html │ ├── tests.py │ └── views.py ├── docker-compose.yml └── nginx ├── Dockerfile └── nginx.conf
Desire to learn how to build this projection? Check out the Dockerizing Django with Postgres, Gunicorn, and Nginx blog post.
Django Storages
Side by side, permit's install django-storages, then that nosotros tin can use Spaces as the main Django storage backend, and boto3, in order to to interact with the Spaces API.
Update the requirements file:
boto3==i.18.36 Django==3.two django-storages==1.11.1 gunicorn==20.one.0
Add storages
to the INSTALLED_APPS
in settings.py:
INSTALLED_APPS = [ 'django.contrib.admin' , 'django.contrib.auth' , 'django.contrib.contenttypes' , 'django.contrib.sessions' , 'django.contrib.messages' , 'django.contrib.staticfiles' , 'upload' , 'storages' , ]
Update the images and spin up the new containers:
$ docker-compose up -d --build
Static Files
Moving along, we need to update the handling of static files in settings.py:
STATIC_URL = '/static/' STATIC_ROOT = BASE_DIR / 'staticfiles' STATICFILES_DIRS = ( BASE_DIR / 'static' ,) MEDIA_URL = '/media/' MEDIA_ROOT = BASE_DIR / 'mediafiles'
Replace those settings with the post-obit:
USE_SPACES = bone . getenv ( 'USE_SPACES' ) == 'Truthful' if USE_SPACES : # settings AWS_ACCESS_KEY_ID = bone . getenv ( 'AWS_ACCESS_KEY_ID' ) AWS_SECRET_ACCESS_KEY = bone . getenv ( 'AWS_SECRET_ACCESS_KEY' ) AWS_STORAGE_BUCKET_NAME = os . getenv ( 'AWS_STORAGE_BUCKET_NAME' ) AWS_DEFAULT_ACL = 'public-read' AWS_S3_ENDPOINT_URL = 'https://nyc3.digitaloceanspaces.com' AWS_S3_OBJECT_PARAMETERS = { 'CacheControl' : 'max-age=86400' } # static settings AWS_LOCATION = 'static' STATIC_URL = f 'https:// { AWS_S3_ENDPOINT_URL } / { AWS_LOCATION } /' STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' else : STATIC_URL = '/static/' STATIC_ROOT = BASE_DIR / 'staticfiles' STATICFILES_DIRS = ( BASE_DIR / 'static' ,) MEDIA_URL = '/media/' MEDIA_ROOT = BASE_DIR / 'mediafiles'
Exist sure to update the
AWS_S3_ENDPOINT_URL
if you're using a different region thanNYC3
.
Have annotation of USE_SPACES
and STATICFILES_STORAGE
:
- The
USE_SPACES
surroundings variable is used to turn the Spaces storage on (value isTruthful
) and off (value isImitation
). So, you could configure ii Docker compose files: One for development with Spaces off and the other for production with Spaces on. - The
STATICFILES_STORAGE
setting configures Django to automatically add static files to the Spaces bucket when thecollectstatic
command is run.
Review the official django-storages documentation on DigitalOcean and Amazon S3 for more info on the above settings and config.
Add together the appropriate environment variables to the web
service in the docker-etch.yml file:
web : build : ./app command : bash -c 'while !</dev/tcp/db/5432; do slumber 1; washed; gunicorn hello_django.wsgi:application --bind 0.0.0.0:8000' volumes : - ./app/:/usr/src/app/ - static_volume:/usr/src/app/staticfiles - media_volume:/usr/src/app/mediafiles expose : - 8000 environment : - SECRET_KEY=please_change_me - SQL_ENGINE=django.db.backends.postgresql - SQL_DATABASE=postgres - SQL_USER=postgres - SQL_PASSWORD=postgres - SQL_HOST=db - SQL_PORT=5432 - DATABASE=postgres - USE_SPACES=TRUE - AWS_ACCESS_KEY_ID=UPDATE_ME - AWS_SECRET_ACCESS_KEY=UPDATE_ME - AWS_STORAGE_BUCKET_NAME=UPDATE_ME depends_on : - db
Don't forget to update
AWS_ACCESS_KEY_ID
andAWS_SECRET_ACCESS_KEY
with the user keys that you just created along with theAWS_STORAGE_BUCKET_NAME
.
To test, re-build and run the containers:
$ docker-etch downwards -five $ docker-etch up -d --build
Collect the static files:
$ docker-etch exec web python manage.py collectstatic
It should take much longer than earlier since the files are being uploaded to the Spaces bucket.
http://localhost:1337 should even so render correctly:
View the folio source to ensure the CSS stylesheet is pulled in from the Spaces bucket:
Verify that the static files can be seen on the DigitalOcean control panel within the "static" subfolder of the Spaces bucket:
Media uploads will still hit the local filesystem since we've but configured Spaces for static files. We'll piece of work with media uploads soon.
Finally, update the value of USE_SPACES
to FALSE
and re-build the images to make sure that Django uses the local filesystem for static files. Once done, change USE_SPACES
back to TRUE
.
To prevent users from overwriting existing static files, media file uploads should exist placed in a different subfolder in the saucepan. We'll handle this past creating custom storage classes for each type of storage.
Add together a new file chosen storage_backends.py to the "app/hello_django" folder:
from django.conf import settings from storages.backends.s3boto3 import S3Boto3Storage class StaticStorage ( S3Boto3Storage ): location = 'static' default_acl = 'public-read' class PublicMediaStorage ( S3Boto3Storage ): location = 'media' default_acl = 'public-read' file_overwrite = False
Make the following changes to settings.py:
USE_SPACES = bone . getenv ( 'USE_SPACES' ) == 'TRUE' if USE_SPACES : # settings AWS_ACCESS_KEY_ID = os . getenv ( 'AWS_ACCESS_KEY_ID' ) AWS_SECRET_ACCESS_KEY = bone . getenv ( 'AWS_SECRET_ACCESS_KEY' ) AWS_STORAGE_BUCKET_NAME = os . getenv ( 'AWS_STORAGE_BUCKET_NAME' ) AWS_DEFAULT_ACL = 'public-read' AWS_S3_ENDPOINT_URL = 'https://nyc3.digitaloceanspaces.com' AWS_S3_OBJECT_PARAMETERS = { 'CacheControl' : 'max-age=86400' } # static settings AWS_LOCATION = 'static' STATIC_URL = f 'https:// { AWS_S3_ENDPOINT_URL } / { AWS_LOCATION } /' STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' # public media settings PUBLIC_MEDIA_LOCATION = 'media' MEDIA_URL = f 'https:// { AWS_S3_ENDPOINT_URL } / { PUBLIC_MEDIA_LOCATION } /' DEFAULT_FILE_STORAGE = 'hello_django.storage_backends.PublicMediaStorage' else : STATIC_URL = '/static/' STATIC_ROOT = BASE_DIR / 'staticfiles' MEDIA_URL = '/media/' MEDIA_ROOT = BASE_DIR / 'mediafiles' STATICFILES_DIRS = ( BASE_DIR / 'static' ,)
With the DEFAULT_FILE_STORAGE
setting now prepare, all FileFields will upload their content to the Spaces bucket. Review the remaining settings before moving on.
Next, let's make a few changes to the upload
app.
app/upload/models.py:
from django.db import models class Upload ( models . Model ): uploaded_at = models . DateTimeField ( auto_now_add = True ) file = models . FileField ()
app/upload/views.py:
from django.conf import settings from django.cadre.files.storage import FileSystemStorage from django.shortcuts import render from .models import Upload def image_upload ( asking ): if request . method == 'POST' : image_file = request . FILES [ 'image_file' ] image_type = request . POST [ 'image_type' ] if settings . USE_SPACES : upload = Upload ( file = image_file ) upload . save () image_url = upload . file . url else : fs = FileSystemStorage () filename = fs . salvage ( image_file . proper name , image_file ) image_url = fs . url ( filename ) return render ( request , 'upload.html' , { 'image_url' : image_url }) return render ( asking , 'upload.html' )
Create the new migration file and then build the new images:
$ docker-compose exec web python manage.py makemigrations $ docker-compose down -v $ docker-compose upwards -d --build $ docker-compose exec web python manage.py drift
Test it out! Upload an image at http://localhost:1337. The image should exist uploaded to Spaces (to the media subfolder) and the image_url
should include the S3 url:
Add a new class to the storage_backends.py:
form PrivateMediaStorage ( S3Boto3Storage ): location = 'private' default_acl = 'individual' file_overwrite = Faux custom_domain = Fake
Add the appropriate settings:
USE_SPACES = os . getenv ( 'USE_SPACES' ) == 'True' if USE_SPACES : # settings AWS_ACCESS_KEY_ID = bone . getenv ( 'AWS_ACCESS_KEY_ID' ) AWS_SECRET_ACCESS_KEY = bone . getenv ( 'AWS_SECRET_ACCESS_KEY' ) AWS_STORAGE_BUCKET_NAME = os . getenv ( 'AWS_STORAGE_BUCKET_NAME' ) AWS_DEFAULT_ACL = 'public-read' AWS_S3_ENDPOINT_URL = 'https://nyc3.digitaloceanspaces.com' AWS_S3_OBJECT_PARAMETERS = { 'CacheControl' : 'max-historic period=86400' } # static settings AWS_LOCATION = 'static' STATIC_URL = f 'https:// { AWS_S3_ENDPOINT_URL } / { AWS_LOCATION } /' STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage' # public media settings PUBLIC_MEDIA_LOCATION = 'media' MEDIA_URL = f 'https:// { AWS_S3_ENDPOINT_URL } / { PUBLIC_MEDIA_LOCATION } /' DEFAULT_FILE_STORAGE = 'hello_django.storage_backends.PublicMediaStorage' # individual media settings PRIVATE_MEDIA_LOCATION = 'private' PRIVATE_FILE_STORAGE = 'hello_django.storage_backends.PrivateMediaStorage' else : STATIC_URL = '/static/' STATIC_ROOT = BASE_DIR / 'staticfiles' MEDIA_URL = '/media/' MEDIA_ROOT = BASE_DIR / 'mediafiles' STATICFILES_DIRS = ( BASE_DIR / 'static' ,)
Create a new model in app/upload/models.py:
from django.db import models from hello_django.storage_backends import PublicMediaStorage , PrivateMediaStorage class Upload ( models . Model ): uploaded_at = models . DateTimeField ( auto_now_add = True ) file = models . FileField ( storage = PublicMediaStorage ()) class UploadPrivate ( models . Model ): uploaded_at = models . DateTimeField ( auto_now_add = True ) file = models . FileField ( storage = PrivateMediaStorage ())
So, update the view:
from django.conf import settings from django.core.files.storage import FileSystemStorage from django.shortcuts import render from .models import Upload , UploadPrivate def image_upload ( asking ): if request . method == 'POST' : image_file = request . FILES [ 'image_file' ] image_type = asking . POST [ 'image_type' ] if settings . USE_SPACES : if image_type == 'private' : upload = UploadPrivate ( file = image_file ) else : upload = Upload ( file = image_file ) upload . save () image_url = upload . file . url else : fs = FileSystemStorage () filename = fs . save ( image_file . name , image_file ) image_url = fs . url ( filename ) return render ( request , 'upload.html' , { 'image_url' : image_url }) return render ( request , 'upload.html' )
Once again, create the migration file, re-build the images, and spin upwards the new containers:
$ docker-compose exec web python manage.py makemigrations $ docker-compose down -v $ docker-compose up -d --build $ docker-etch exec web python manage.py drift
To test, upload a private prototype at http://localhost:1337. Like a public image, the paradigm should be uploaded to Spaces (to the private subfolder) and the image_url
should include the Spaces URL along with the following query string parameters:
- AWSAccessKeyId
- Signature
- Expires
Substantially, we created a temporary, signed URL that users can access for a specific flow of time. You lot won't be able to access it directly, without the parameters.
Conclusion
This postal service walked you through how to create a saucepan on DigitalOcean Spaces and prepare Django to upload and serve static files and media uploads to and from Spaces.
By using Spaces, you:
- Increment the amount of space you lot accept available for static and media files
- Decrease the stress on your ain server since it no longer has to serve upwardly the files
- Tin can limit access to specific files
- Can take reward of a CDN
Let us know if we missed annihilation or if y'all have any other tips and tricks. You can find the final lawmaking in the django-digitalocean-spaces repo.
morseasereardscon.blogspot.com
Source: https://testdriven.io/blog/django-digitalocean-spaces/
0 Response to "Upload File to Space Digitalocean With Python"
Postar um comentário