2011-12-31 11 views
17

Quiero servir a mi comprime CSS/JS de CloudFront (viven en S3), pero no puedo encontrar la manera de hacerlo a través de los ajustes del compresor en settings.py, tengo los siguientes:Django-compressor: cómo escribir en S3, leer de CloudFront?

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 la

COMPRESS_URL, mis archivos están siendo leídos 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 leerlo de CloudFront. es posible?

+0

vieron su boleto en github ... ¿le importaría publicar tu solución? – Jiaaro

+0

Mis más sinceras disculpas por no haber visto esto antes, publicaré mi solución a continuación mañana (con suerte) –

Respuesta

33

me escribió un backend de almacenamiento envoltura alrededor de la proporcionada por boto

miaplicacion/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' 
+0

Miraré ASAP @Jiao. Implementé mi propia solución de hack, pero verifico esto contra el mío y veo cómo funciona. Volveré y te registraré tan pronto como confirme! Gracias de cualquier manera. –

11

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' 

Compressor Docs

Esta solución anterior guarda los archivos localmente y los carga en 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.

Adición de gzip añade una arruga:

settings.py

AWS_IS_GZIPPED = True 

aunque esto dio como resultado un error siempre que un archivo compresible (css y js según almacenamientos) estaba siendo empujado a s3 durante collectstatic:

AttributeError: 'cStringIO.StringO' object has no attribute 'name'

Esto fue debido a algún error extraño tener que ver con la compresión de los archivos css/js que yo 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 arriba (y se proporciona en el compresor docs).

nueva 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 se guardan todos los archivos .css y .js (excluyendo los archivos de administración, que me sirven sin comprimir desde CloudFront) mientras empuja el resto de los archivos a S3 (y no molesto por guardarlos localmente, aunque podría agregar fácilmente la línea self.local_storage._save).

Pero cuando corro compresa, quiero que mi Js comprimidos y archivos .css a quedarse relegados a s3 así que crear otra sublcass para el compresor de empleo:

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 

Por último, teniendo en cuenta estas nuevas subclases, me necesita 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 al respecto.

+0

Esto fue genial y funcionó a la perfección. Gracias por la ayuda. –

4

parece que el problema se fija en realidad contra la corriente en Django, https://github.com/django/django/commit/5c954136eaef3d98d532368deec4c19cf892f664

El método _get_size problemática probablemente podría parcheado localmente para trabajar alrededor de ella para versiones anteriores de Django.

EDITAR: Eche un vistazo a https://github.com/jezdez/django_compressor/issues/100 para un trabajo real alrededor.

+0

Este trabajo ofrece errores de profundidad de recursividad para mí. Acabo de copiar el parche pegado como está, cambié la clase de almacenamiento predeterminada por la nueva y encendí el indicador AWS_IS_GZIPPED. – sbidwai

+0

Tengo el mismo problema, ¿hay alguna noticia sobre qué lo causa o qué ha cambiado desde que funcionó? Muchas gracias por cualquier pista! – tiwei

3

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 arreglar eso.

FWIW, también hay https://bitbucket.org/david/django-storages/pull-request/32/s3boto-gzip-fix-and-associated-unit-tests que soluciona otro problema de guardar archivos en S3 al tener AWS_IS_GZIPPED configurado en True. Qué yak ha sido.

1

Además, para la transmisión de las distribuciones es útil para anular la función url para permitir rtmp:// direcciones URL, 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) 
Cuestiones relacionadas