python - storages - Compresor Django: ¿cómo escribir en S3, leer en CloudFront?
django s3 (5)
Quiero servir mi CSS / JS comprimido de CloudFront (viven en S3), pero no puedo encontrar la forma de hacerlo a través de la configuración del compresor en settings.py, tengo lo siguiente:
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
A pesar de COMPRESS_URL, mis archivos se están leyendo desde mi cubo s3:
<link rel="stylesheet" href="https://example.s3.amazonaws.com/compressed_static/css/e0684a1d5c25.css?Signature=blahblahblah;Expires=farfuture;AWSAccessKeyId=blahblahblah" type="text/css" />
Supongo que el problema es que quiero escribir el archivo en S3, pero lo leo desde CloudFront. es posible?
Además, para las distribuciones de transmisión, es útil anular la función url
para permitir rtmp://
urls, como en:
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)
En realidad, esto también parece ser un problema en django-storage. Cuando el compresor compara los valores hash de los archivos en S3, django-storage no descomprime el contenido de los archivos Gzip, sino que intenta comparar diferentes valores hash. Abrí https://bitbucket.org/david/django-storages/pull-request/33/fix-gzip-support para solucionarlo.
FWIW, también hay https://bitbucket.org/david/django-storages/pull-request/32/s3boto-gzip-fix-and-associated-unit-tests que corrige otro problema de guardar archivos en S3 al tener AWS_IS_GZIPPED establecido en True. Qué yak ha sido.
Escribí un backend de almacenamiento de envoltura alrededor del proporcionado por 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)
Donde mi archivo settings.py tiene ...
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''
Hice algunos cambios diferentes en 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''
Esta solución anterior guardaba los archivos localmente y los subía a s3. Esto me permite comprimir los archivos fuera de línea. Si no está comprimiendo gz, lo anterior debería funcionar para servir archivos comprimidos de CloudFront.
Agregar gzip agrega una arruga:
settings.py
AWS_IS_GZIPPED = True
aunque esto dio lugar a un error cada vez que un archivo comprimible (css y js según los almacenes) se estaba enviando a s3 durante la recopilación de datos:
AttributeError: el objeto ''cStringIO.StringO'' no tiene ningún atributo ''nombre''
Esto se debió a un error extraño que tenía que ver con la compresión de los archivos css / js que no entiendo. Estos archivos los necesito localmente, descomprimidos y no en s3, así que podría evitar el problema por completo si modifico la subclase de almacenamiento a la que se hizo referencia anteriormente (y que se proporciona en los documentos del compresor).
nuevo 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
Esto luego guardó todos los archivos .css y .js (excluyendo los archivos de administración, que sirvo sin comprimir de CloudFront) mientras empujaba el resto de los archivos a s3 (y no molestaba en guardarlos localmente, aunque fácilmente podía agregar self.local_storage). _save line).
Pero cuando ejecuto compress, quiero que mis archivos comprimidos .js y .css sean enviados a s3, así que creo otra sublcass para compresor para usar:
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
Finalmente, dadas estas nuevas subclases, necesito actualizar algunas configuraciones:
COMPRESS_STORAGE = ''example_app.storage.CachedS3BotoStorage'' #from the docs (linked below)
STATICFILES_STORAGE = ''example_app.storage.StaticToS3Storage''
Y eso es todo lo que tengo que decir sobre eso.
Parece que el problema se corrigió en Django, https://github.com/django/django/commit/5c954136eaef3d98d532368deec4c19cf892f664
El método problemático _get_size probablemente podría parchearse localmente para evitar versiones anteriores de Django.
EDITAR: Eche un vistazo a https://github.com/jezdez/django_compressor/issues/100 para un verdadero trabajo.