Làm cách nào để thiết lập một dự án Django với django-storages và Amazon S3, nhưng với các thư mục khác nhau cho tệp tĩnh và tệp phương tiện?


92

Tôi đang định cấu hình một dự án Django đang sử dụng hệ thống tệp máy chủ để lưu trữ các tệp tĩnh ứng dụng ( STATIC_ROOT) và tệp do người dùng tải lên ( MEDIA_ROOT).

Bây giờ tôi cần lưu trữ tất cả nội dung đó trên S3 của Amazon, vì vậy tôi đã tạo một nhóm cho việc này. Bằng cách sử dụng django-storagesvới phần botophụ trợ lưu trữ, tôi đã quản lý để tải các ảnh tĩnh đã thu thập được lên nhóm S3:

MEDIA_ROOT = '/media/'
STATIC_ROOT = '/static/'

DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
AWS_ACCESS_KEY_ID = 'KEY_ID...'
AWS_SECRET_ACCESS_KEY = 'ACCESS_KEY...'
AWS_STORAGE_BUCKET_NAME = 'bucket-name'
STATICFILES_STORAGE = 'storages.backends.s3boto.S3BotoStorage'

Sau đó, tôi gặp sự cố: cái MEDIA_ROOTSTATIC_ROOTkhông được sử dụng trong nhóm, vì vậy gốc của nhóm chứa cả tệp tĩnh và đường dẫn do người dùng tải lên.

Vì vậy, sau đó tôi có thể đặt:

S3_URL = 'http://s3.amazonaws.com/%s' % AWS_STORAGE_BUCKET_NAME
STATIC_URL = S3_URL + STATIC_ROOT
MEDIA_URL = 'S3_URL + MEDIA_ROOT

Và sử dụng các cài đặt đó trong các mẫu, nhưng không có sự phân biệt giữa các tệp tĩnh / phương tiện khi lưu trữ trong S3 với django-storages.

Làm thế nào điều này có thể được thực hiện?

Cảm ơn!


8
Bởi vì chỉ có một cài đặt để chỉ định tên của nhóm ( AWS_STORAGE_BUCKET_NAME) và đó là cài đặt được sử dụng khi một phiên bản của lớp được chỉ định trong STATICFILES_STORAGEđược khởi tạo.
Armando Pérez Marqués

Câu trả lời:


126

Tôi nghĩ cách sau sẽ hoạt động và đơn giản hơn phương pháp của Mandx, mặc dù nó rất giống nhau:

Tạo s3utils.pytệp:

from storages.backends.s3boto import S3BotoStorage

StaticRootS3BotoStorage = lambda: S3BotoStorage(location='static')
MediaRootS3BotoStorage  = lambda: S3BotoStorage(location='media')

Sau đó, trong settings.py:

DEFAULT_FILE_STORAGE = 'myproject.s3utils.MediaRootS3BotoStorage'
STATICFILES_STORAGE = 'myproject.s3utils.StaticRootS3BotoStorage'

Có thể thấy một ví dụ khác nhưng có liên quan (mà tôi đã thực sự thử nghiệm) trong hai example_tệp ở đây .


1
Chắc chắn đơn giản hơn và tốt hơn phiên bản của tôi. Mặc dù tôi đã không thử nghiệm điều này, tôi cũng nghĩ rằng điều này sẽ hiệu quả. Cảm ơn! Tôi cũng đang kiểm tra kho lưu trữ django-s3storage của bạn , có vẻ như một giải pháp rất nhẹ nếu dự án sử dụng riêng S3.
Armando Pérez Marqués,

1
Và, nếu bạn thích đóng gói hơn, hãy xem django-s3-folder-storage . Tôi vừa mới tìm thấy nó, không thể biết liệu nó có phải là giải pháp tương tự này không nhưng được đóng gói sẵn.
Armando Pérez Marqués

4
Điều này không hiệu quả với tôi, các tệp phương tiện được tải lên / của nhóm s3. Có vẻ như cài đặt vị trí không được tôn trọng. django-kho == 1.1.6, django-extensions == 1.1.1, django = 1,4
Nathan Keller

3
Cho nó với tôi nó có ý nghĩa hơn để có xô riêng biệt và tôi không giống như có cấu hình bên ngoài của các xác lập cá mô-đun nên giải pháp của tôi đã kết thúc như thế này gist.github.com/antonagestam/6075199
antonagestam

1
Những giải pháp này không hoạt động, từ những gì tôi có thể nói. Điều này sẽ là cách tiếp cận: gist.github.com/defrex/82680e858281d3d3e6e4
defrex

8

Tôi hiện đang sử dụng mã này trong một s3utilsmô-đun riêng biệt :

from django.core.exceptions import SuspiciousOperation
from django.utils.encoding import force_unicode

from storages.backends.s3boto import S3BotoStorage


def safe_join(base, *paths):
    """
    A version of django.utils._os.safe_join for S3 paths.

    Joins one or more path components to the base path component intelligently.
    Returns a normalized version of the final path.

    The final path must be located inside of the base path component (otherwise
    a ValueError is raised).

    Paths outside the base path indicate a possible security sensitive operation.
    """
    from urlparse import urljoin
    base_path = force_unicode(base)
    paths = map(lambda p: force_unicode(p), paths)
    final_path = urljoin(base_path + ("/" if not base_path.endswith("/") else ""), *paths)
    # Ensure final_path starts with base_path and that the next character after
    # the final path is '/' (or nothing, in which case final_path must be
    # equal to base_path).
    base_path_len = len(base_path) - 1
    if not final_path.startswith(base_path) \
       or final_path[base_path_len:base_path_len + 1] not in ('', '/'):
        raise ValueError('the joined path is located outside of the base path'
                         ' component')
    return final_path


class StaticRootS3BotoStorage(S3BotoStorage):
    def __init__(self, *args, **kwargs):
        super(StaticRootS3BotoStorage, self).__init__(*args, **kwargs)
        self.location = kwargs.get('location', '')
        self.location = 'static/' + self.location.lstrip('/')

    def _normalize_name(self, name):
        try:
            return safe_join(self.location, name).lstrip('/')
        except ValueError:
            raise SuspiciousOperation("Attempted access to '%s' denied." % name)


class MediaRootS3BotoStorage(S3BotoStorage):
    def __init__(self, *args, **kwargs):
        super(MediaRootS3BotoStorage, self).__init__(*args, **kwargs)
        self.location = kwargs.get('location', '')
        self.location = 'media/' + self.location.lstrip('/')

    def _normalize_name(self, name):
        try:
            return safe_join(self.location, name).lstrip('/')
        except ValueError:
            raise SuspiciousOperation("Attempted access to '%s' denied." % name)

Sau đó, trong mô-đun cài đặt của tôi:

DEFAULT_FILE_STORAGE = 'myproyect.s3utils.MediaRootS3BotoStorage'
STATICFILES_STORAGE = 'myproyect.s3utils.StaticRootS3BotoStorage'

Tôi phải xác định lại _normalize_name()phương thức riêng tư để sử dụng phiên bản "cố định" của safe_join()hàm, vì mã ban đầu cung cấp cho tôi SuspiciousOperationngoại lệ cho các đường dẫn hợp pháp.

Tôi đăng bài này để xem xét, nếu ai có thể đưa ra câu trả lời tốt hơn hoặc cải thiện câu trả lời này, nó sẽ rất được hoan nghênh.


7

Tệp: PROJECT_NAME / custom_storages.py

from django.conf import settings
from storages.backends.s3boto import S3BotoStorage

class StaticStorage(S3BotoStorage):
    location = settings.STATICFILES_LOCATION

class MediaStorage(S3BotoStorage):
    location = settings.MEDIAFILES_LOCATION

Tệp: PROJECT_NAME / settings.py

STATICFILES_LOCATION = 'static'
MEDIAFILES_LOCATION = 'media'

if not DEBUG:
    STATICFILES_STORAGE = 'PROJECT_NAME.custom_storages.StaticStorage'
    DEFAULT_FILE_STORAGE = 'PROJECT_NAME.custom_storages.MediaStorage'
    AWS_ACCESS_KEY_ID = 'KEY_XXXXXXX'
    AWS_SECRET_ACCESS_KEY = 'SECRET_XXXXXXXXX'
    AWS_STORAGE_BUCKET_NAME = 'BUCKET_NAME'
    AWS_HEADERS = {'Cache-Control': 'max-age=86400',}
    AWS_QUERYSTRING_AUTH = False

Và chạy: python manage.py collectstatic


Nếu bạn tình cờ đặt tên cho tệp này storages.pythay vì custom_storages.pyBạn sẽ muốn sử dụngfrom __future__ import absolute_import
Aaron McMillin

2

Tôi nghĩ câu trả lời khá đơn giản và được thực hiện theo mặc định. Điều này đang làm việc cho tôi trên AWS Elastic Beanstalk với Django 1.6.5 và Boto 2.28.0:

STATICFILES_FINDERS = (
    'django.contrib.staticfiles.finders.FileSystemFinder',
    'django.contrib.staticfiles.finders.AppDirectoriesFinder',
)

TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.Loader',
    'django.template.loaders.app_directories.Loader',
)

DEFAULT_FILE_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
STATICFILES_STORAGE = 'storages.backends.s3boto.S3BotoStorage'
AWS_ACCESS_KEY_ID = os.environ['AWS_ACCESS_KEY_ID']
AWS_SECRET_ACCESS_KEY = os.environ['AWS_SECRET_KEY']

Các khóa AWS được chuyển vào từ tệp cấu hình vùng chứa và tôi không có STATIC_ROOThoặc không có STATIC_URLthiết lập nào. Ngoài ra, không cần s3utils.pytệp. Các chi tiết này được xử lý bởi hệ thống lưu trữ tự động. Mẹo ở đây là tôi cần tham chiếu đường dẫn không xác định này trong các mẫu của mình một cách chính xác và động. Ví dụ:

<link rel="icon" href="{% static "img/favicon.ico" %}">

Đó là cách tôi giải quyết biểu tượng yêu thích của mình đang tồn tại cục bộ (trước khi triển khai) ~/Projects/my_app/project/my_app/static/img/favicon.ico.

Tất nhiên tôi có một local_settings.pytệp riêng để truy cập nội dung này cục bộ trong môi trường nhà phát triển và nó có cài đặt STATIC và MEDIA. Tôi đã phải thực hiện rất nhiều thử nghiệm và đọc để tìm ra giải pháp này và nó hoạt động ổn định mà không có lỗi.

Tôi hiểu rằng bạn cần phân tách tĩnh và gốc và xem xét rằng bạn chỉ có thể cung cấp một nhóm, tôi sẽ chỉ ra rằng phương pháp này lấy tất cả các thư mục trong môi trường cục bộ của tôi ~/Projects/my_app/project/my_app/static/và tạo một thư mục trong nhóm gốc (tức là: S3bucket / img / như trong ví dụ trên). Vì vậy, bạn có thể tách các tệp. Ví dụ: bạn có thể có một mediathư mục trong staticthư mục và truy cập nó thông qua tạo mẫu với điều này:

{% static "media/" %}

Tôi hi vọng cái này giúp được. Tôi đến đây để tìm kiếm câu trả lời và cố gắng tìm kiếm một giải pháp đơn giản hơn là mở rộng hệ thống lưu trữ. Thay vào đó, tôi đọc tài liệu về mục đích sử dụng của Boto và tôi thấy rằng rất nhiều thứ tôi cần đã được tích hợp sẵn theo mặc định. Chúc mừng!


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.