Python Django压缩器:如何写入S3,从CloudFront读取?
我想从CloudFront提供压缩后的CSS/JS(它们位于S3上),但无法通过settings.py中的compressor settings解决问题,我有以下几点:Python Django压缩器:如何写入S3,从CloudFront读取?,python,django,amazon-s3,amazon-cloudfront,django-compressor,Python,Django,Amazon S3,Amazon Cloudfront,Django Compressor,我想从CloudFront提供压缩后的CSS/JS(它们位于S3上),但无法通过settings.py中的compressor settings解决问题,我有以下几点: COMPRESS_OFFLINE = True COMPRESS_URL = 'http://static.example.com/' #same as STATIC_URL, so unnecessary, just here for simplicity COMPRESS_STORAGE = 'my
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
尽管有压缩URL,我的文件仍从s3存储桶中读取:
我想问题是我想将文件写入S3,但从CloudFront读取它。这可能吗?我围绕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)
my 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'
我对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'
AWS_IS_GZIPPED = True
上述解决方案将文件保存在本地,并将其上载到s3。这让我可以脱机压缩文件。如果您不是gzipping,以上内容应该适用于从CloudFront提供压缩文件
添加gzip会增加皱纹:
设置.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'
AWS_IS_GZIPPED = True
尽管这会导致在静态收集过程中将可压缩文件(根据存储的css和js)推送到s3时出错:
AttributeError:'cStringIO.StringO'对象没有属性'name'
这是由于一些奇怪的错误,与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_存储。_保存行)
但当我运行compress时,我希望压缩后的.js和.css文件被推送到s3,因此我创建了另一个子容器供compressor使用:
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'
这就是我要说的。看起来问题实际上是在Django上游解决的 有问题的get_size方法可能会在本地进行修补,以解决Django旧版本的问题
编辑:查看实际解决方法。实际上,这似乎也是django存储中的一个问题。当compressor比较S3上文件的散列时,django storages不会解压缩Gzip文件的内容,而是尝试比较不同的散列。我已经准备好了
FWIW,它还修复了另一个问题,即当AWS__gzip设置为True时,实际将文件保存到S3。这真是一只牦牛。此外,对于流媒体发行版,重写
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)
看起来CloudFront现在提供了内置的压缩功能。如果启用,则向CloudFront发出请求。如果CF没有存储压缩的缓存版本,它会向原始服务器(S3)发出请求,原始服务器返回未压缩的文件。CloudFront随后将自动压缩文件,将其存储在缓存中,并将其提供给查看器 通过编辑发行版中的“行为”,可以在CF中启用自动压缩。在底部,它会询问“自动压缩文件”,您可以将其另存为“是” 对此的p.S.要求:
在权限中,更改CORS以显示内容长度,即
在github上看到您的票证。。。你介意发布你的解决方案吗?我为没有早点看到这一点表示诚挚的歉意,我明天会在下面发布我的解决方案(希望如此)。另一个窍门是转到你的CloudFront发行版>“编辑”行为>,并在底部显示“自动压缩对象”的地方单击“是”并保存。我会尽快查看@jiao。我已经实现了我自己的黑客解决方案,但我将对照我的解决方案进行检查,看看它是如何工作的。我一确认就会回来给你检查!不管怎样,谢谢。这项工作给了我递归深度的错误。我刚刚复制粘贴了你的补丁,将默认的存储类更改为新的,并打开了AWS_is_gzip标志。我也有同样的问题,有没有关于它的原因或自它工作以来发生了什么变化的消息?谢谢你的提示!