Джанго-компрессор: как писать в S3, читать из CloudFront? - PullRequest
17 голосов
/ 31 декабря 2011

Я хочу обслуживать свои сжатые 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="https://example.s3.amazonaws.com/compressed_static/css/e0684a1d5c25.css?Signature=blahblahblah;Expires=farfuture;AWSAccessKeyId=blahblahblah" type="text/css" />

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

Ответы [ 5 ]

33 голосов
/ 17 января 2012

Я написал бэкэнд-хранилище вокруг того, который предоставлен 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'
11 голосов
/ 24 января 2012

Я сделал несколько разных изменений в 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.Это позволило мне сжать файлы в автономном режиме.Если вы не распаковываете файлы, вышеприведенное должно работать для обслуживания сжатых файлов из 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 (и не мешает сохранять их локально, хотя может легко добавить строку self.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'

И это все, что мне нужноскажи об этом.

4 голосов
/ 03 мая 2012

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

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

РЕДАКТИРОВАТЬ: Естьвзгляните на https://github.com/jezdez/django_compressor/issues/100 для реальной работы.

3 голосов
/ 10 мая 2012

На самом деле, это, похоже, проблема в django-хранилищах.Когда компрессор сравнивает хэши файлов на S3, django-хранилища не распаковывают содержимое файлов Gzip, а пытаются сравнить разные хэши.Я открыл 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.Что это был за як.

1 голос
/ 02 июля 2012

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

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)
...