[ python3 ] via Django-Storages upload to S3 with Date path and ContentDisposition.

Photo by Harley-Davidson on Unsplash

最近寫 Side-Projcet 花了很多時間卡在這種鬼地方,於官方的文件,還是一些線上的參考資料中,都沒有獲得有用的資訊,最後還直接看 Django-Storages 的 Source Code 才找到解法,其問題是覺得該套件沒有做好管線處理,導致許多功能都只能依賴它的方法,並且不易變化。

目錄

Preface

幾個禮拜前才寫了一篇 Python 相關的文章 「[Python 3] 檢查檔案及目錄是否存在 ,自動更名,避免複寫。」而已,最近又因為跳坑,開始寫 WebApp,主要是使用 Python 與 Django 框架開發的一個簡易上傳功能的網站,並搭載縮網址功能,可惜沒有打算 Open Source ((笑

首先參考了不少網路上的教學資源,也翻過 Buffer Overflow ,完全無功而返,查看完Django-Storages的 Source Code 之後才發現事情好像沒這麼簡單,功能幾乎都是寫死的,由於自身功力不足,基本上就是透過 Class 的方式去複寫Django-Storages上的函式庫,雖然也覺得是個笨方法,但也沒更好的解藥了,至於為什麼網路上的教學沒有用,我想可能是版本差異,所以方法不可行。

Environment

  • Dgango 3.6+
  • Django 3.0+
  • Django-Storages 1.9.1

Preceding Operation

基本上就是建置好你的 Virtual Environment 與安裝好 Django 、 Django-Storages,並設置好 Settings.py 。

AWS_ACCESS_KEY_ID = ""
AWS_SECRET_ACCESS_KEY = ""
AWS_STORAGE_BUCKET_NAME = ""
AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com'

AWS_STATIC_LOCATION = 'static'
STATICFILES_STORAGE = 'webapp.storage_backends.StaticStorage'
STATIC_URL = f"https://{AWS_S3_CUSTOM_DOMAIN}/{AWS_STATIC_LOCATION}/"

AWS_PRIVATE_MEDIA_LOCATION = 'media/private'
PRIVATE_FILE_STORAGE = 'webapp.storage_backends.PrivateMediaStorage'
AWS_DEFAULT_ACL = None

然後設置 Models ,關於 path_from_date 的功能就是設置日期路徑,針對上傳的時間建立樹狀日期目錄。

class File_Info(models.Model):

    def path_from_date(instance, filename):
        date = datetime.today()
        if date:
            year = str(date.year)
            month = str(date.month).zfill(2)
            day = str(date.day).zfill(2)

        return f'{year}/{month}/{day}/{filename}'

    id = models.AutoField(db_column='ID', primary_key=True)

    media_storage = PrivateMediaStorage()
    file_name = models.FileField(upload_to=path_from_date,
                                 storage= media_storage
    )
    # file_name = models.ImageField(storage=PrivateMediaStorage())
    content_type = models.ForeignKey(db_column='Content_type', null=True)
    file_url = models.URLField(db_column='File_URL', max_length=1600)

    class Meta:
        managed = True
        db_table = 'File_Info'

建立 storage_backends.py ,基本上就是把整個 Source 整個貼過來,然後加上 #Add This 那兩行,如果要新增其他元素也可以從這邊加上,但比較麻煩的就是遞值。

from django.conf import settings
from storages.backends.s3boto3 import S3Boto3Storage
from os import SEEK_SET

class PrivateMediaStorage(S3Boto3Storage):
    location = settings.AWS_PRIVATE_MEDIA_LOCATION
    default_acl = 'private'
    file_overwrite = False
    custom_domain = False

    def _save(self, name, content):
        cleaned_name = self._clean_name(name)
        name = self._normalize_name(cleaned_name)
        params = self._get_write_parameters(name, content)

        if (self.gzip and
                params['ContentType'] in self.gzip_content_types and
                'ContentEncoding' not in params):
            content = self._compress_content(content)
            params['ContentEncoding'] = 'gzip'

        # Add This ...
        filename = name.split('/')[-1]
        params['ContentDisposition'] = f'attachment; filename="{filename}"'
        # end

        encoded_name = self._encode_name(name)
        obj = self.bucket.Object(encoded_name)
        if self.preload_metadata:
            self._entries[encoded_name] = obj

        content.seek(0, SEEK_SET)
        obj.upload_fileobj(content, ExtraArgs=params)
        return cleaned_name

Upload File to S3

然後這邊就是 views 的部分,透過 urls.py 設置路由到 save_file 完成上傳的動作。

def save_file(request, file):
    file_info = VideoUploadForm(request.POST, request.FILES)
    file = file_info.cleaned_data["file"]

    file_bin = MorkFiledata(    
                file_name= file,
                content_type= MorkContenttype.objects.get(name= file.content_type),
    )
    file_bin.save()

Result

可以看見上傳至 S3 之後的檔案路徑,除了一開始設置的根路徑為 location = settings.AWS_PRIVATE_MEDIA_LOCATION 也就是 media/private 以外,後方的路徑就依照日期年、月、日來建立資料夾,並存放檔案。

via Django-Storages upload to S3 with ContentDisposition.
via Django Storages upload to S3 with ContentDisposition Success.

Reference

https: / / s t a c k o v e r f l o w . c o m / q u e s t i o n s / 4 3 2 0 8 4 0 1 / a d d – d y n a m i c – c o n t e n t – d i s p o s i t i o n – f o r – f i l e – n a m e s a m a z o n – s 3 – i n – p y t h o n
h t t p s : / / d j a n g o – s t o r a g e s . r e a d t h e d o c s . i o / e n / l a t e s t / b a c k e n d s / a m a z o n – S 3 . h t m l
h t t p s : / /g i t h u b . c o m / e t i a n e n / d j a n g o – s 3 – s t o r a g e


發佈留言

這個網站採用 Akismet 服務減少垃圾留言。進一步瞭解 Akismet 如何處理網站訪客的留言資料