storages python django amazon-s3 amazon-cloudfront django-compressor

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''

Compresor Docs

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.