2010-05-01 7 views
5

Memorizzo le immagini caricate dall'utente nel datastore di Google App Engine come db.Blob, come proposto in the docs. Quindi servo quelle immagini su /images/<id>.jpg.Inviare un "304 non modificato" per le immagini memorizzate nel datastore

Il server invia sempre una risposta 200 OK, il che significa che il browser deve scaricare la stessa immagine più volte (== più lentamente) e che il server deve inviare la stessa immagine più volte (== più costoso).

Poiché la maggior parte di quelle immagini probabilmente non cambierà mai, mi piacerebbe essere in grado di inviare una risposta 304 Not Modified. Sto pensando di calcolare una sorta di hash dell'immagine quando l'utente carica, e poi lo uso per sapere se l'utente ha già questa immagine (forse inviare l'hash come Etag?)

ho trovato this answer e this answer che spiegare la logica abbastanza bene, ma ho 2 domande:

  1. e 'possibile inviare una Etag in Google App Engine?
  2. Qualcuno ha implementato tale logica e/o è disponibile uno snippet di codice?

risposta

7

Bloggart utilizza questa tecnica. Dai un'occhiata a this blog post.

class StaticContentHandler(webapp.RequestHandler): 
    def output_content(self, content, serve=True): 
    self.response.headers['Content-Type'] = content.content_type 
    last_modified = content.last_modified.strftime(HTTP_DATE_FMT) 
    self.response.headers['Last-Modified'] = last_modified 
    self.response.headers['ETag'] = '"%s"' % (content.etag,) 
    if serve: 
     self.response.out.write(content.body) 
    else: 
     self.response.set_status(304) 

    def get(self, path): 
    content = get(path) 
    if not content: 
     self.error(404) 
     return 

    serve = True 
    if 'If-Modified-Since' in self.request.headers: 
     last_seen = datetime.datetime.strptime(
      self.request.headers['If-Modified-Since'], 
      HTTP_DATE_FMT) 
     if last_seen >= content.last_modified.replace(microsecond=0): 
     serve = False 
    if 'If-None-Match' in self.request.headers: 
     etags = [x.strip('" ') 
       for x in self.request.headers['If-None-Match'].split(',')] 
     if content.etag in etags: 
     serve = False 
    self.output_content(content, serve) 
+0

Eccellente esempio! ;) –

+0

Ho implementato la mia soluzione in base a questo esempio e tutto funziona correttamente. Grazie a jbochi e Nick! – Emilien

0

Potrebbe esserci una soluzione più semplice qui. Ciò richiede che non si sovrascrivano mai i dati associati a nessun identificatore, ad es. la modifica dell'immagine creerebbe un nuovo id (e quindi un nuovo URL).

Basta impostare l'intestazione Expires dal gestore richieste al futuro remoto, ad es. ora + un anno. Ciò comporterebbe la memorizzazione nell'immagine dei client e la mancata richiesta di aggiornamento fino a quel momento.

Questo approccio ha alcuni compromessi, come assicurarsi che i nuovi URL siano incorporati quando le immagini vengono modificate, quindi devi decidere tu stesso. Quello che jbochi sta proponendo è l'altra alternativa che mette più logica nel gestore di richieste di immagini.

+0

Questa sarebbe davvero una possibile soluzione. Come ho detto, non mi aspetto che le immagini cambino, ma non si sa mai. Ho intenzione di esaminare la soluzione di Nick, poiché avrei altri contenuti statici di quelle immagini, e quelle potrebbero cambiare, quindi avere una soluzione per tutti sembra la migliore. Grazie comunque! – Emilien

0

A proposito, grazie a webob, webapp.RequestHandler offre un modo semplice per controllare If-None-Match.

if etag in self.request.if_none_match: 
    pass # do something 
0

perché sarebbe l'uso di codice questo:

self.response.headers['ETag'] = '"%s"' % (content.etag,) 

invece di questo:

self.response.headers['ETag'] = '"%s"' % content.etag 

penso che sia lo stesso e utilizzerà il 2 ° a meno che qualcuno spiega il ragionamento.

Problemi correlati