17

voglio servire il mio compresso CSS/JS da CloudFront (vivono sulla S3), ma sono in grado di capire come farlo tramite le impostazioni del compressore in settings.py, ho il seguente:Django-compressor: come scrivere su S3, leggere da 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 

Nonostante la COMPRESS_URL, i miei file vengono letti dal mio secchio S3:
<link rel="stylesheet" href="https://example.s3.amazonaws.com/compressed_static/css/e0684a1d5c25.css?Signature=blahblahblah;Expires=farfuture;AWSAccessKeyId=blahblahblah" type="text/css" />

Credo che il problema è che voglio scrivere il file su S3, ma leggerlo da CloudFront. È possibile?

+0

visto il tuo biglietto su github ... ti dispiacerebbe pubblicare la tua soluzione? – Jiaaro

+0

Le mie scuse sincere per non averlo visto prima, pubblicherò la mia soluzione in basso domani (si spera) –

risposta

33

ho scritto un backend di memorizzazione wrapper quello fornito da boto

frontend/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) 

Dove il mio file settings.py ha ...

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

esamineremo al più presto @Jiaao. Ho implementato la mia soluzione hacker, ma controllerò la mia contro la mia e vedrò come funziona. Tornerà e ti controllerò appena confermerò! Grazie in entrambi i casi. –

11

Ho apportato alcune modifiche diverse a 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

Questa soluzione precedente ha salvato i file localmente e li ha caricati su s3. Questo mi consente di comprimere i file offline. Se non stai facendo il gzipping, quanto sopra dovrebbe funzionare per servire file compressi da CloudFront.

Aggiunta gzip aggiunge una grinza:

settings.py

AWS_IS_GZIPPED = True 

se questo ha determinato un errore quando un file comprimibile (css e js secondo stoccaggi) veniva spinto a s3 durante collectstatic:

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

Questo è stato a causa di qualche errore bizzarro hanno a che fare con la compressione dei file js/css che non capisco. Questi file mi servono localmente, decompressi e non su s3, così potrei evitare del tutto il problema se modificherò la sottoclasse di memorizzazione di cui sopra (e fornita nel compressore docs).

nuovo 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 

Questo poi salvati tutti i css e js (esclusi i file di amministrazione, che che servo non compresso da CloudFront) mentre si spinge il resto dei file da S3 (e non preoccuparsi di salvarli localmente, anche se potrebbe facilmente aggiungere la linea self.local_storage._save).

Ma quando corro impacco, voglio che i miei js compressi e file css per ottenere spinto a s3 così creo un'altra sublcass per compressore da utilizzare:

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 

Infine, date queste nuove sottoclassi, ho è necessario aggiornare alcune impostazioni:

COMPRESS_STORAGE = 'example_app.storage.CachedS3BotoStorage' #from the docs (linked below) 
STATICFILES_STORAGE = 'example_app.storage.StaticToS3Storage' 

E questo è tutto ciò che ho da dire al riguardo.

+0

Questo è stato fantastico e ha funzionato perfettamente. Grazie per l'aiuto. –

4

sembra che il problema è stato effettivamente risolto a monte Django, https://github.com/django/django/commit/5c954136eaef3d98d532368deec4c19cf892f664

Il metodo _get_size problematica potrebbe probabilmente patch a livello locale per lavorare intorno ad esso per le versioni precedenti di Django.

EDIT: Date un'occhiata al https://github.com/jezdez/django_compressor/issues/100 per un lavoro effettivo intorno.

+0

Questo lavoro mi dà errori di profondità di ricorsione. Ho appena copiato la patch, ho cambiato la classe di archiviazione predefinita con quella nuova e ho attivato il flag AWS_IS_GZIPPED. – sbidwai

+0

Ho lo stesso problema, c'è qualche novità su cosa lo causa o cosa è cambiato da quando ha funzionato? Grazie mille per ogni suggerimento! – tiwei

3

In realtà questo sembra essere un problema anche nei django-storages. Quando il compressore confronta gli hash dei file su S3, i django-storages non decomprimono il contenuto dei file Gzip, ma cerca di confrontare diversi hash. Ho aperto https://bitbucket.org/david/django-storages/pull-request/33/fix-gzip-support per risolvere il problema.

FWIW, c'è anche https://bitbucket.org/david/django-storages/pull-request/32/s3boto-gzip-fix-and-associated-unit-tests che corregge un altro problema relativo al salvataggio effettivo dei file su S3 quando AWS_IS_GZIPPED è impostato su True. Che yak è stato.

1

Inoltre, per lo streaming distribuzioni è utile per ignorare la funzione di permettere urlrtmp:// gli URL, come in:

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)