Подтвердить что ты не робот

Django-compressor: как писать на S3, читать с CloudFront?

Я хочу использовать сжатый CSS/JS от CloudFront (они живут на S3), но я не могу решить, как это сделать с помощью настроек компрессора в settings.py, у меня есть следующее:

    COMPRESS_OFFLINE = True 
    COMPRESS_URL = 'http://static.example.com/' #same as STATIC_URL, so unnecessary, just here for simplicity
    COMPRESS_STORAGE = 'my_example_dir.storage.CachedS3BotoStorage' #subclass suggested in [docs][1]
    COMPRESS_OUTPUT_DIR = 'compressed_static'
    COMPRESS_ROOT = '/home/dotcloud/current/static/' #location of static files on server

Несмотря на COMPRESS_URL, мои файлы считываются из моего ведра s3:
<link rel="stylesheet" href="#" onclick="location.href='https://example.s3.amazonaws.com/compressed_static/css/e0684a1d5c25.css?Signature=blahblahblah;Expires=farfuture;AWSAccessKeyId=blahblahblah'; return false;" type="text/css" />

Я думаю, проблема в том, что я хочу записать файл на S3, но прочитал его с CloudFront. Возможно ли это?

4b9b3361

Ответ 1

Я написал бэкэнд для хранения обертки вокруг объекта, предоставленного boto

MyApp/storage_backends.py:

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

def domain(url):
    return urlparse.urlparse(url).hostname    

class MediaFilesStorage(S3BotoStorage):
    def __init__(self, *args, **kwargs):
        kwargs['bucket'] = settings.MEDIA_FILES_BUCKET
        kwargs['custom_domain'] = domain(settings.MEDIA_URL)
        super(MediaFilesStorage, self).__init__(*args, **kwargs)

class StaticFilesStorage(S3BotoStorage):
    def __init__(self, *args, **kwargs):
        kwargs['bucket'] = settings.STATIC_FILES_BUCKET
        kwargs['custom_domain'] = domain(settings.STATIC_URL)
        super(StaticFilesStorage, self).__init__(*args, **kwargs)

Где мой файл settings.py имеет...

STATIC_FILES_BUCKET = "myappstatic"
MEDIA_FILES_BUCKET = "myappmedia"
STATIC_URL = "http://XXXXXXXX.cloudfront.net/"
MEDIA_URL = "http://XXXXXXXX.cloudfront.net/"

DEFAULT_FILE_STORAGE = 'myapp.storage_backends.MediaFilesStorage'
COMPRESS_STORAGE = STATICFILES_STORAGE = 'myapp.storage_backends.StaticFilesStorage'

Ответ 2

Я сделал несколько разных изменений в settings.py

AWS_S3_CUSTOM_DOMAIN = 'XXXXXXX.cloudfront.net' #important: no "http://"
AWS_S3_SECURE_URLS = True #default, but must set to false if using an alias on cloudfront

COMPRESS_STORAGE = 'example_app.storage.CachedS3BotoStorage' #from the docs (linked below)
STATICFILES_STORAGE = 'example_app.storage.CachedS3BotoStorage'

Документы для компрессоров

Это вышеописанное решение сохранили файлы локально, а также загрузили их на s3. Это позволяет мне сжимать файлы в автономном режиме. Если вы не используете gzipping, то это должно работать для подачи сжатых файлов из CloudFront.

Добавление gzip добавляет морщинку:

settings.py

AWS_IS_GZIPPED = True

хотя это привело к ошибке, когда сжимаемый файл (css и js в соответствии с хранилищами) был нажат на s3 во время collectstatic:

AttributeError: объект 'cStringIO.StringO' не имеет атрибута 'name'

Это произошло из-за некоторой причудливой ошибки, связанной с сжатием файлов css/js, которые я не понимаю. Эти файлы мне нужны локально, распакованы, а не на s3, поэтому я мог бы вообще избежать проблемы, если я подстрою подклассы хранения, упомянутые выше (и предоставленные в компрессоре docs).

new storage.py

from os.path import splitext 
from django.core.files.storage import get_storage_class  
from storages.backends.s3boto import S3BotoStorage  


class StaticToS3Storage(S3BotoStorage): 

    def __init__(self, *args, **kwargs): 
        super(StaticToS3Storage, self).__init__(*args, **kwargs) 
        self.local_storage = get_storage_class('compressor.storage.CompressorFileStorage')() 

    def save(self, name, content): 
        ext = splitext(name)[1] 
        parent_dir = name.split('/')[0] 
        if ext in ['.css', '.js'] and not parent_dir == 'admin': 
            self.local_storage._save(name, content) 
        else:     
            filename = super(StaticToS3Storage, self).save(name, content) 
            return filename 

Затем были сохранены все файлы .css и .js(за исключением файлов администрирования, которые я обслуживаю без сжатия от CloudFront), в то время как остальные файлы сохраняются на s3 (и не беспокоят их локально, хотя они могут легко добавить .local_storage._save).

Но когда я запускаю сжатие, я хочу, чтобы мои сжатые .js и .css файлы попадали на s3, поэтому я создаю еще один подкласс для использования компрессором:

class CachedS3BotoStorage(S3BotoStorage): 
        """ 
        django-compressor uses this class to gzip the compressed files and send them to s3 
        these files are then saved locally, which ensures that they only create fresh copies 
        when they need to 
        """ 
        def __init__(self, *args, **kwargs): 
            super(CachedS3BotoStorage, self).__init__(*args, **kwargs) 
            self.local_storage = get_storage_class('compressor.storage.CompressorFileStorage')() 


        def save(self, filename, content): 
            filename = super(CachedS3BotoStorage, self).save(filename, content) 
            self.local_storage._save(filename, content) 
            return filename 

Наконец, учитывая эти новые подклассы, мне нужно обновить несколько настроек:

COMPRESS_STORAGE = 'example_app.storage.CachedS3BotoStorage' #from the docs (linked below)
STATICFILES_STORAGE = 'example_app.storage.StaticToS3Storage'

И это все, что я должен сказать об этом.

Ответ 3

Похоже, что проблема действительно была зафиксирована вверх по потоку в Django, https://github.com/django/django/commit/5c954136eaef3d98d532368deec4c19cf892f664

Проблемный метод _get_size, возможно, может быть исправлен локально, чтобы обойти его для более старых версий Django.

EDIT: посмотрите на https://github.com/jezdez/django_compressor/issues/100 для реальной работы.

Ответ 4

На самом деле это тоже проблема в django-хранилищах. Когда компрессор сравнивает хэши файлов на S3, django-storage не распаковывает содержимое файлов Gzip'ed, но пытается сравнить разные хэши. Я открыл https://bitbucket.org/david/django-storages/pull-request/33/fix-gzip-support, чтобы исправить это.

FWIW, также есть https://bitbucket.org/david/django-storages/pull-request/32/s3boto-gzip-fix-and-associated-unit-tests, который исправляет еще одну проблему фактического сохранения файлов на S3, когда AWS_IS_GZIPPED установлен в значение True. Какой як был.

Ответ 5

Кроме того, для потоковых распределений полезно переопределить функцию url, чтобы разрешить URL-адреса rtmp://, как в:

import urlparse
class VideoStorageForCloudFrontStreaming(S3BotoStorage):
    """
    Use when needing rtmp:// urls for a CloudFront Streaming distribution. Will return
    a proper CloudFront URL.

    Subclasses must be sure to set custom_domain.
    """
    def url(self, name):
        name = urlparse.quote(self._normalize_name(self._clean_name(name)))
        return "rtmp://%s/cfx/st/%s" % (self.custom_domain, name)

    # handy for JW Player:
    @Property
    def streamer(self):
        return "rtmp://%s/cfx/st" % (self.custom_domain)