使用Python和Django将文件上传到AWS S3

Uploading Files to AWS S3 with Python and Django

介绍

为了建立更多的交互式网站,我们不仅将信息传递给用户,还允许他们上传自己的数据。 这为我们的网站为最终用户提供了更多机会和更多方式。

通过允许用户上传文件,我们可以允许他们与他人共享照片,视频或音乐,或者将其备份以保存。 我们还可以提供功能来管理文件并将其通过网站转换为其他格式,而不是安装本机应用程序。

社交媒体在全球范围内的兴起可以归因于用户上传文件的能力,这些能力主要以图像和视频的形式供其他用户查看,也可以作为一种通信手段。 通过使用户能够将文件上传到网站和平台,通信手段得到了增强,信息现在可以以许多不同的格式传播。

在本文中,我们将探讨Django如何处理文件上传,以及如何利用云存储来利用和扩展此功能来满足我们的需求。

Django如何处理文件存储

Django不仅允许我们将概念转变为Web应用程序,而且还为我们提供了处理文件的功能,并允许用户将文件上传到我们的Web应用程序以进行进一步的交互。 通过表单,用户可以将文件附加到他们的请求中,并将文件上传并存储在我们的后端服务器中。

在保存文件之前,先将其临时存储在某个位置,然后再进行处理并存储在预期的最终位置。 例如,如果上传的文件小于2.5MB,则该文件的内容将存储在内存中,然后在处理文件时完成所有操作后将其写入磁盘。

这样可以快速处理小文件。 对于大于2.5MB的文件,在接收数据时首先将它们写入一个临时位置,然后在处理完成后将文件移至其最终目的地。

Django中的文件行为可以通过各种设置进行自定义,例如FILE_UPLOAD_MAX_MEMORY_SIZE,它允许我们修改2.5MB的上传限制大小,以便将文件首先写入内存而不是临时写入。 我们还可以通过FILE_UPLOAD_PERMISSIONS为上传的文件配置默认权限。

其他设置可以在官方Django文档的本部分中找到。

我们可以在哪里存储文件?

在基于Django的Web应用程序中,我们可以将上传的文件存储在各个不同的位置。 我们可以将它们存储在部署Django代码的我们自己的服务器上,也可以将它们发送到可能在其他地方设置用于存储目的的其他服务器。

为了降低服务器维护成本并提高性能,我们还可以选择不将上传的文件存储在我们自己的服务器上。 在这种情况下,我们可以将它们移交给其他托管存储提供商,例如AWS,Azure或OneDrive。

有几个软件包可以让我们与我们提到的各种服务提供商提供的API进行交互。 它们包括:

  • Django-Storages使我们能够将上传的文件存储到AWS Simple Storage Service(S3),Dropbox,OneDrive,Azure和Digital Ocean。

  • Django-S3-Storage,我们可以通过它直接将文件上传到Amazon S3

  • Django-Cumulus,它使我们能够与Rackspace进行交互以满足存储需求

  • 其他包括Django-Dropbox,Django-Storage-Swift和Django-Cloudinary-Storage。 在这里可以找到更多

  • 在本文中,我们将使用Django-s3direct包将文件存储在AWS S3上。

    我们的应用程序-Django Drive

    我们将使用Django构建一个Web应用程序,在其中将上载内容供最终用户查看。 这将通过使用框架附带的Django管理界面来实现。

    我们的网站将用于销售汽车,并在其上显示详细信息并添加正在销售的汽车的图像或视频。

    待售汽车的图像或视频将存储在S3中。 为简便起见,我们暂时不实施用户注册或登录。

    建立

    我们将使用Pipenv来设置和管理隔离的环境,在该环境中,我们将通过运行以下命令以使用Python3进行设置来构建Django应用程序:

    1
    $ pipenv install --three

    设置好环境后,我们现在可以安装Django和Django-s3direct来处理向S3的文件上传:

    1
    $ pipenv install django django-s3direct

    在开始实现应用程序的核心功能之前,Django提供了一组命令来引导我们的项目。 我们的Django驱动器项目将只有一个应用程序,这将是本文的重点。 为此,我们运行以下命令:

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

    django-admin startproject ...命令创建项目,而django-admin startapp ...命令创建应用程序。

    我们设置的最后一步是通过运行migrate命令来创建数据库表:

    1
    $ python manage.py migrate

    通过运行命令python manage.py runserver启动项目时,以下页面将对我们表示欢迎,该页面确认我们的设置成功:

    uploading files setup complete

    由于我们将文件上传到AWS S3,因此我们需要为演示目的设置一个免费的AWS账户。 设置完成后,我们可以导航到S3仪表板并创建一个包含我们上传内容的新存储桶。

    为了使Django-s3direct与我们的AWS设置进行交互,我们需要提供以下凭证AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEYAWS_STORAGE_BUCKET_NAME

    接下来,我们将以下内容添加到我们的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允许我们为上传指定多个目标,这样我们可以将不同的文件定向到单独的S3存储桶。 对于此项目,我们会将所有上传文件放入一个存储桶中。 另一个不错的功能是我们还可以限制可以上传到我们网站的文件类型。 就我们而言,我们仅将其限制为MP4视频,JPEG和PNG图像。

    注意:有关设置Django-s3direct的更多详细信息,例如CORS和Access Setup,可以在这里找到。

    我们还需要在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')),
        ...
    ]

    实作

    我们将从为汽车数据创建模型开始,该模型将显示给最终用户。 该模型还将定义在将汽车添加到平台时将在管理仪表板上输入的信息。 车型如下:

    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}"

    对于每辆汽车,我们将存储其名称,制造年份,价格以及图像或视频。 创建模型之后,让我们进行迁移以在数据库中创建表,该表将通过运行以下命令来保存我们的数据:

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

    由于我们将使用Django管理控制台来管理平台上的汽车,因此我们需要在django_drive_app/admin.py中注册我们的模型:

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

    admin.site.register(Car)

    然后,我们需要通过运行以下命令并按照提示来创建负责添加汽车的超级用户:

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

    python manage.py runserver命令只是重新启动我们的应用程序。

    重新启动服务器后,我们现在可以导航到http://127.0.0.1:8000/admin上的管理仪表板,并使用我们先前指定的凭据登录。 在站点管理下,我们可以看到DJANGO_DRIVE_APP,其中包含添加或更改现有汽车的选项。

    这是我们用来添加汽车及其详细信息的表格:

    django application admin add car

    保存汽车后,我们可以在AWS控制台的S3存储桶中找到上传的图像。 这意味着我们的文件已上传到AWS。

    现在,我们将创建一个视图,以向我们的网站的最终用户显示汽车及其数据,并显示与每辆汽车相关的图像或视频。 我们将从在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

    在此视图中,我们使用基于类的Django视图来呈现HTML文件以显示我们的汽车。 在我们看来,我们运行查询以获取存储在数据库中的所有汽车。

    接下来,让我们创建django_drive_app/templates/django_drive_app/cars.html来渲染我们的汽车:

    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>
                 {{ car.name }} ({{ car.year_of_manufacture }})  
                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>
           
          {% endfor %}
        </div>
      </body>
    </html>

    放置好视图和模板后,让我们通过创建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"),
    ]

    我们导入视图并添加URL条目以将端点映射到将渲染汽车的视图。 当我们重新启动服务器并导航到127.0.0.1:8000/cars/时,会遇到以下情况:

    django app car landing page

    如我们所见,我们创建了带有附加图像和视频的汽车,并将其上传到AWS的S3服务。 从AWS提取后,相同的图像和视频已在我们的Web应用程序中呈现。

    结论

    在本文中,我们创建了一个简单的Django应用程序,允许管理员通过Django管理仪表板将文件上传到AWS S3。 我们在目标网页上的S3上托管了上传的文件,包括用户希望购买或观看的汽车的视频和图像。

    我们使用了Django-s3direct库来处理Django应用程序与存储文件的AWS S3之间的交互。 通过Django管理应用程序,我们能够在最终目标页面上载最终呈现给最终用户的文件。 我们能够上传和渲染图像和视频。

    该项目的源代码可在GitHub上找到。