18

我想从 CloudFront 提供我的压缩 CSS/JS(它们位于 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 读取它。这可能吗?

4

6 回答 6

33

我围绕 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'
于 2012-01-17T02:22:33.250 回答
11

我对 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。这让我可以离线压缩文件。如果您不使用 gzip,则上述内容应该适用于从 CloudFront 提供压缩文件。

添加gzip会增加皱纹:

设置.py

AWS_IS_GZIPPED = True

尽管在 collectstatic 期间将可压缩文件(根据存储的 css 和 js)推送到 s3 时,这会导致错误:

AttributeError:“cStringIO.StringO”对象没有属性“名称”

这是由于一些奇怪的错误与我不理解的 css/js 文件的压缩有关。这些文件我需要在本地,解压缩,而不是在 s3 上,所以如果我调整上面引用的存储子类(并在压缩器文档中提供),我可以完全避免这个问题。

新存储.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 行)。

但是当我运行 compress 时,我希望我压缩的 .js 和 .css 文件被推送到 s3,所以我创建了另一个 sublcas 供压缩器使用:

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'

这就是我要说的。

于 2012-01-24T05:00:16.410 回答
4

似乎问题实际上已在 Django 上游修复,https://github.com/django/django/commit/5c954136eaef3d98d532368deec4c19cf892f664

有问题的 _get_size 方法可能会在本地修补以解决旧版本 Django 的问题。

编辑:查看https://github.com/jezdez/django_compressor/issues/100以了解实际解决方法。

于 2012-05-03T16:12:22.613 回答
3

实际上,这似乎也是 django-storages 中的一个问题。当压缩器比较 S3 上文件的哈希值时,django-storages 不会解压缩 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。真是一头牦牛。

于 2012-05-10T10:26:03.413 回答
1

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)
于 2012-07-01T21:34:45.597 回答
0

看起来 CloudFront 现在提供内置压缩功能。如果启用它,则会向 CloudFront 发出请求。如果 CF 没有存储压缩缓存版本,它会向返回未压缩文件的源服务器 (S3) 发出请求。然后,CloudFront 将自动压缩文件,将其存储在缓存中并将其提供给查看器。

您可以通过在您的发行版中编辑“行为”来启用 CF 中的自动压缩。在它询问“自动压缩文件”的底部,您可以将其保存为是。

PS要求:

在权限更改 CORS 以显示 Content-Length 即 <AllowedHeader>Content-Length</AllowedHeader>

更多信息在这里

于 2020-07-23T08:02:22.937 回答