5 votes

Envoyer un "304 Non modifié" pour les images stockées dans le datastore

Je stocke les images téléchargées par l'utilisateur dans le datastore de Google App Engine en tant que db.Blob, comme proposé dans les docs. Je sers ensuite ces images sur /images/.jpg.

Le serveur envoie toujours une réponse 200 OK, ce qui signifie que le navigateur doit télécharger la même image plusieurs fois (== plus lent) et que le serveur doit envoyer la même image plusieurs fois (== plus cher).

Comme la plupart de ces images ne changeront probablement jamais, j'aimerais être en mesure de renvoyer une réponse 304 Not Modified. Je pense à calculer une sorte de hash de l'image lorsque l'utilisateur l'envoie, et ensuite l'utiliser pour savoir si l'utilisateur possède déjà cette image (peut-être envoyer le hash en tant qu' Etag?)

J'ai trouvé cette réponse et cette réponse qui expliquent assez bien la logique, mais j'ai 2 questions:

  1. Est-il possible d'envoyer un Etag dans Google App Engine?
  2. Quelqu'un a-t-il mis en œuvre une telle logique, et/ou existe-t-il un extrait de code disponible?

8voto

jbochi Points 12280

Bloggart utilise cette technique. Jetez un œil à cet article de blog.

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)

0voto

Snowman Points 101

Il pourrait y avoir une solution plus simple ici. Cela requiert que vous ne remplaciez jamais les données associées à aucun identifiant, par exemple, modifier l'image créerait un nouvel identifiant (et donc une nouvelle URL).

Définissez simplement l'en-tête Expires depuis votre gestionnaire de requêtes jusqu'à une date future lointaine, par exemple maintenant + un an. Cela permettrait aux clients de mettre en cache l'image et de ne pas demander de mise à jour jusqu'à ce que le moment arrive.

Cette approche comporte des compromis, comme s'assurer que de nouvelles URLs sont intégrées lorsque des images sont modifiées, donc vous devez décider par vous-même. Ce que jbochi propose est l'autre alternative qui introduit plus de logique dans le gestionnaire de requêtes d'image.

0voto

WGH Points 407

En passant, grâce à webob, webapp.RequestHandler fournit un moyen facile de vérifier If-None-Match.

if etag in self.request.if_none_match:
    pass # faire quelque chose

0voto

peawormsworth Points 156

Pourquoi le code utiliserait-il ceci :

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

au lieu de ceci :

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

Je pense que c'est la même chose et j'utiliserais le 2ème sauf si quelqu'un explique la raison.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X