Subir archivos a AWS S3 con Python y Django

AWS S3 es un excelente servicio de bajo costo para el almacenamiento de archivos. En este artículo, utilizaremos Python y Django para cargar archivos en AWS S3.

Introducción

En la búsqueda de crear sitios web más interactivos, no solo transmitimos información a los usuarios, sino que también les permitimos cargar sus propios datos. Esto abre más oportunidades y más formas en que nuestros sitios web pueden servir a los usuarios finales.

Al permitir que los usuarios carguen archivos, podemos permitirles compartir fotografías, videos o música con otros o hacer una copia de seguridad de ellos para su custodia. También podemos proporcionar la funcionalidad para administrar archivos y convertirlos a otros formatos a través de sitios web en lugar de instalar aplicaciones nativas.

El auge de las redes sociales a nivel mundial se puede atribuir a la capacidad de los usuarios para cargar sus archivos, principalmente en forma de imágenes y videos para que los vean otros usuarios y también como medio de comunicación. Al permitir que los usuarios carguen archivos en sitios web y plataformas, se han mejorado los medios de comunicación y ahora la información se puede difundir en muchos formatos diferentes.

En esta publicación, exploraremos cómo Django maneja la carga de archivos y cómo podemos aprovechar y ampliar esta funcionalidad con el almacenamiento en la nube para satisfacer nuestras necesidades.

Cómo maneja Django el almacenamiento de archivos

Django no solo nos permite convertir conceptos en aplicaciones web, sino que también nos brinda funcionalidad para manejar archivos y permitir que los usuarios carguen archivos en nuestras aplicaciones web para una mayor interacción. A través de formularios, los usuarios pueden adjuntar archivos a sus solicitudes y hacer que sus archivos se carguen y almacenen en nuestros servidores backend.

Antes de guardar un archivo, se almacena temporalmente en algún lugar antes de procesarlo y almacenarlo en la ubicación final prevista. Por ejemplo, si el archivo cargado tiene menos de 2,5 MB, el contenido de ese archivo se almacenará en la memoria y luego se escribirá en el disco una vez que se completen todas las operaciones mientras se procesa.

Esto hace que el proceso sea rápido para archivos pequeños. Para los archivos de más de 2,5 MB, primero se escriben en una ubicación temporal a medida que se reciben los datos, luego, una vez que se completa el procesamiento, el archivo se mueve a su destino final.

El comportamiento de los archivos en Django se puede personalizar a través de varias configuraciones, como FILE_UPLOAD_MAX_MEMORY_SIZE, que nos permite modificar el tamaño límite de carga de 2,5 MB para los archivos que se escriben primero en la memoria y no en una ubicación temporal. También podemos configurar los permisos predeterminados para los archivos subidos a través de FILE_UPLOAD_PERMISSIONS.

Otras configuraciones se pueden encontrar en esta sección de la documentación oficial de Django.

¿Dónde podemos almacenar nuestros archivos? {#dónde podemos almacenar nuestros archivos}

En una aplicación web impulsada por Django, podemos almacenar los archivos cargados en varias ubicaciones diferentes. Podemos almacenarlos en nuestros propios servidores donde se implementa el código Django, o podemos enviarlos a otros servidores que pueden haberse configurado en otro lugar para fines de almacenamiento.

En un intento por reducir los costos de mantenimiento del servidor y mejorar el rendimiento, también podemos optar por no almacenar los archivos cargados en nuestros propios servidores. En este caso, podemos transferirlos a otros proveedores de almacenamiento hospedado como AWS, Azur, o OneDrive, entre otros.

Existen varios paquetes que nos permiten interactuar con las APIs proporcionadas por los distintos proveedores de servicios que hemos mencionado. Incluyen:

Para esta publicación, utilizaremos el paquete Django-s3direct para almacenar nuestros archivos en el S3 de AWS.

Nuestra aplicación - Django Drive

Usaremos Django para crear una aplicación web en la que cargaremos contenido para que lo vean los usuarios finales. Esto se logrará mediante el uso de la interfaz de administración de Django, que viene con el marco.

Nuestro sitio se utilizará para vender autos y en él mostraremos detalles y agregaremos imágenes o videos de los autos en venta.

Las imágenes o videos de los autos en venta se almacenarán en S3. No implementaremos el registro de usuario o el inicio de sesión en este momento por brevedad.

Configuración

Usaremos Pipenv para configurar y administrar nuestro entorno aislado en el que construiremos nuestra aplicación Django ejecutando el siguiente comando para configurarlo usando Python3:

1
$ pipenv install --three

Con el entorno configurado, ahora podemos instalar Django y Django-s3direct para manejar nuestras cargas de archivos a S3:

1
$ pipenv install django django-s3direct

Django proporciona un conjunto de comandos para iniciar nuestro proyecto antes de que comencemos a implementar la funcionalidad central de nuestra aplicación. Nuestro proyecto de disco Django tendrá una sola aplicación que será el foco de esta publicación. Para lograr esto, ejecutamos los siguientes comandos:

1
2
$ django-admin startproject django_drive && cd django_drive
$ django-admin startapp django_drive_app

El comando django-admin startproject ... crea el proyecto, y el comando django-admin startapp ... crea la aplicación.

El último paso de nuestra configuración es crear tablas de base de datos ejecutando el comando migrar:

1
$ python manage.py migrate

Cuando comenzamos nuestro proyecto ejecutando el comando python manage.py runserver, nos da la bienvenida la siguiente página, que confirma que nuestra configuración fue exitosa:

uploading files setup complete

Dado que cargaremos nuestros archivos en AWS S3, necesitaremos configurar una cuenta AWS de nivel gratuito para propósitos de demostración. Después de la configuración, podemos navegar al panel de S3 y crear un nuevo depósito que contendrá nuestras cargas.

Para que Django-s3direct interactúe con nuestra configuración de AWS, debemos proporcionar las siguientes credenciales AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY y AWS_STORAGE_BUCKET_NAME.

A continuación, agregaremos lo siguiente a nuestro archivo django_drive/settings.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
AWS_ACCESS_KEY_ID = 'aws-access-key-id'
AWS_SECRET_ACCESS_KEY = 'secret-access-key'
AWS_STORAGE_BUCKET_NAME = 'name-of-the-bucket'
AWS_S3_REGION_NAME = 'name-of-the-region'
AWS_S3_ENDPOINT_URL = 'https://s3.amazonaws.com'

S3DIRECT_DESTINATIONS = {
    'primary_destination': {
        'key': 'uploads/',
        'allowed': ['image/jpg', 'image/jpeg', 'image/png', 'video/mp4'],
    },
}

Django-s3direct nos permite especificar más de un destino para nuestras cargas, de esta manera podemos dirigir diferentes archivos a cubos S3 separados. Para este proyecto, pondremos todas las cargas en un depósito. Otra característica ingeniosa es que también podemos limitar los tipos de archivos que se pueden cargar en nuestro sitio web. En nuestro caso, lo hemos limitado solo a videos MP4, JPEG e imágenes PNG.

Nota: Se pueden encontrar más detalles sobre la configuración de Django-s3direct, como CORS y Configuración de acceso [aquí](https://github.com/bradleyg/django-s3direct#access -configuración).

También necesitamos agregar las siguientes entradas en el archivo django_drive/urls.py:

1
2
3
4
5
6
7
8
from django.urls import path, include

urlpatterns = [
    ...
    path('', include('django_drive_app.urls')),
    path('s3direct/', include('s3direct.urls')),
    ...
]

Implementación

Comenzaremos creando el modelo para los datos de nuestro automóvil, que se mostrará a los usuarios finales. Este modelo también definirá la información que ingresaremos en nuestro panel de administración al agregar autos a nuestra plataforma. El modelo de coche será el siguiente:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
from django.db import models
from s3direct.fields import S3DirectField

class Car(models.Model):
    name = models.CharField(max_length=255, blank=False, null=False)
    year_of_manufacture = models.CharField(max_length=255, blank=False, null=False)
    price = models.CharField(max_length=255, blank=False, null=False)
    image = S3DirectField(dest='primary_destination', blank=True)
    video = S3DirectField(dest='primary_destination', blank=True)

    def __str__(self):
        return f"{self.name} ({self.year_of_manufacture}) - {self.price}"

Para cada automóvil, almacenaremos su nombre, año de fabricación, precio y una imagen o video. Después de crear el modelo, hagamos migraciones para crear la tabla en la base de datos que contendrá nuestros datos ejecutando:

1
2
$ python manage.py makemigrations
$ python manage.py migrate

Dado que usaremos el panel de administración de Django para administrar los autos en nuestra plataforma, debemos registrar nuestro modelo en django_drive_app/admin.py:

1
2
3
4
from django.contrib import admin
from.models import Car

admin.site.register(Car)

Luego, debemos crear el superusuario que estará a cargo de agregar los autos ejecutando el siguiente comando y siguiendo las indicaciones:

1
2
$ python manage.py createsuperuser
$ python manage.py runserver

El comando python manage.py runserver simplemente reinicia nuestra aplicación.

Después de reiniciar nuestro servidor, ahora podemos navegar al panel de administración en http://127.0.0.1:8000/admin e iniciar sesión con las credenciales que especificamos anteriormente. En la administración del sitio, podemos ver nuestra DJANGO_DRIVE_APP con la opción de agregar o cambiar autos existentes.

Este es el formulario que usamos para agregar un auto y sus detalles:

administrador de la aplicación django añadir coche

Una vez que guardamos nuestro coche, podemos encontrar la imagen que hemos subido en nuestro depósito S3 en la consola de AWS. Esto significa que nuestro archivo se ha subido a AWS.

Ahora crearemos una vista para mostrar los autos y sus datos a los usuarios finales de nuestro sitio web y también mostraremos las imágenes o videos asociados con cada auto. Comenzaremos creando una vista en django_drive_app/views.py:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
from django.shortcuts import render
from django.views.generic import TemplateView
from .models import Car

class CarView(TemplateView):
    template_name = 'django_drive_app/cars.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['cars'] = Car.objects.all()
        return context

En esta vista, usamos una vista de Django basada en clases para renderizar el archivo HTML para mostrar nuestros autos. Desde nuestro punto de vista, ejecutamos una consulta para obtener todos los autos almacenados en nuestra base de datos.

A continuación, creemos django_drive_app/templates/django_drive_app/cars.html para renderizar nuestros autos:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE html>
<html>
  <head>
    <title>Django Drive</title>
  </head>
  <body>
    <h3>Welcome to Django Drive. </h3>
    <p>Here are the current cars available for sale: </p>
    <div class="cars-container">
      {% for car in cars %}
        <div class="car">
          <p>
            <b> {{ car.name }} ({{ car.year_of_manufacture }}) </b> <br>
            Price: {{ car.price }}
          </p>
          <!-- if the car has an image attached -->
          {% if car.image %}
          <img src="{{ car.image }}" height="200" width="400"/>
          {% endif %}
          <!-- If the car has a video -->
          {% if car.video %}
            <video width="320" height="240" controls>
                <source src="{{ car.video }}" type="video/mp4">
              Your browser does not support the video tag.
            </video>
          {% endif %}
        </div>
        <hr>
      {% endfor %}
    </div>
  </body>
</html>

Con la vista y la plantilla en su lugar, agreguemos el punto final que se usará para mostrar la lista de autos a los usuarios finales creando django_drive_app/urls.py:

1
2
3
4
5
6
from django.conf.urls import url
from .views import CarView

urlpatterns = [
  url(r'^cars/$', CarView.as_view(), name="cars"),
]

Importamos nuestra vista y agregamos una entrada de URL para asignar el punto final a la vista que representará los autos. Cuando reiniciamos nuestro servidor y navegamos a 127.0.0.1:8000/cars/, encontramos lo siguiente:

Página de destino del coche de la aplicación Django

Como podemos ver, creamos autos con imágenes y videos adjuntos y los subimos al servicio S3 de AWS. Las mismas imágenes y videos se han renderizado en nuestra aplicación web después de obtenerlos de AWS.

Conclusión

En este artículo, hemos creado una aplicación Django simple que permite a los administradores cargar archivos en AWS S3 a través del panel de administración de Django. Presentamos los archivos cargados como alojados en S3 en nuestra página de inicio, incluidos videos e imágenes de los autos que los usuarios desearían comprar o ver.

Usamos la biblioteca Django-s3direct para manejar la interacción entre nuestra aplicación Django y AWS S3 donde se almacenan nuestros archivos. A través de la aplicación de administración de Django, pudimos cargar archivos que finalmente se entregaron a los usuarios finales en nuestra página de destino. Pudimos subir y renderizar tanto imágenes como videos.

El código fuente de este proyecto está disponible aquí en GitHub. .