Gestionnaires Blobstore d'applications Web

Une application Web inclut des classes de gestionnaire de requêtes permettant d'utiliser l'API Blobstore. BlobstoreUploadHandler fournit une logique d'analyse de la requête d'importation transmise via le Blobstore aux enregistrements BlobInfo pour un traitement ultérieur. BlobstoreDownloadHandler facilite la diffusion des valeurs Blobstore à partir de n'importe quel chemin d'accès.

BlobstoreUploadHandler

Les valeurs sont ajoutées au Blobstore par le biais d'importations de fichiers effectuées par les utilisateurs ou administrateurs de l'application. L'application poste un formulaire Web avec un champ d'importation de fichiers et une action de formulaire qui dirige le transfert vers le Blobstore. L'application obtient l'URL d'action du formulaire en appelant une fonction (create_upload_url()), en lui transmettant l'URL d'un gestionnaire d'application qui est appelé lorsque les utilisateurs importent des fichiers. Une application webapp peut utiliser une sous-classe de la classe BlobstoreUploadHandler en tant que gestionnaire de cette URL.

La méthode get_uploads() renvoie une liste d'objets BlobInfo, un pour chaque fichier importé dans la requête. Chaque objet contient la clé Blobstore de la valeur importée, ainsi que des métadonnées telles que le nom du fichier et sa taille. Chaque fichier importé possède également une entité correspondante dans le datastore contenant ces informations. Vous pouvez donc récupérer l'objet BlobInfo ultérieurement à l'aide d'une clé blob ou effectuer une requête de datastore sur les champs de métadonnées. Le gestionnaire d'importation analyse ces informations directement à partir des données de la requête, et non pas à partir du datastore.

Par défaut, get_uploads() renvoie des objets BlobInfo pour tous les fichiers transférés de la requête. La méthode accepte également un argument field_name afin de ne récupérer que le ou les fichiers d'un champ d'importation de fichiers donné. La valeur renvoyée est toujours une liste, qui peut être vide.

class PhotoUploadHandler(blobstore_handlers.BlobstoreUploadHandler):
    def post(self):
        upload = self.get_uploads()[0]
        user_photo = UserPhoto(
            user=users.get_current_user().user_id(),
            blob_key=upload.key())
        user_photo.put()

        self.redirect('/view_photo/%s' % upload.key())

Utiliser BlobstoreUploadHandler avec Google Cloud Storage

Si vous utilisez ce gestionnaire d'importation avec Cloud Storage, vous devez obtenir et stocker le nom de fichier complet de l'objet Cloud Storage, car il est nécessaire pour récupérer le fichier à nouveau à partir de Cloud Storage. Utilisez la fonction get_file_infos, qui renvoie une liste d'enregistrements FileInfo correspondant à chaque importation. Le nom complet de l'objet Cloud Storage, le type de contenu, l'heure de création et d'autres données sont disponibles dans FileInfo (voir le lien pour plus de détails).

BlobstoreDownloadHandler

Pour diffuser une valeur Blobstore, l'application définit l'en-tête X-AppEngine-BlobKey sur la valeur d'une clé du Blobstore, au format chaîne. Lorsque App Engine reçoit cet en-tête dans la réponse, il diffuse la valeur de l'objet blob dans le corps de la réponse. La classe BlobstoreDownloadHandler du gestionnaire webapp facilite la définition de cette valeur dans la réponse.

La méthode send_blob() utilise un objet BlobKey, une clé de chaîne ou un objet BlobInfo comme argument blob_key_or_info, et définit les données de réponse de sorte que la valeur blob soit transmise à l'utilisateur. Elle utilise un argument content_type facultatif qui remplace le type de contenu MIME de la valeur blob stockée. Par défaut, l'objet blob est diffusé avec le type de contenu défini par le client qui l'a importé, un type de contenu généré à partir du nom de fichier ou un type générique si aucune autre information de type n'est disponible.

La méthode send_blob() accepte un argument save_as qui détermine si les données blob sont envoyées en tant que données de réponse brutes ou en tant que pièce jointe MIME avec un nom de fichier. Si l'argument est une chaîne, le blob est envoyé en pièce jointe et la valeur de la chaîne est utilisée comme nom de fichier. Si la valeur est True et que blob_key_or_info est un objet BlobInfo, le nom de fichier de l'objet est utilisé. Par défaut, les données blob sont envoyées dans le corps de la réponse et non pas sous la forme d'une pièce jointe MIME.

class ViewPhotoHandler(blobstore_handlers.BlobstoreDownloadHandler):
    def get(self, photo_key):
        if not blobstore.get(photo_key):
            self.error(404)
        else:
            self.send_blob(photo_key)

Le Blobstore prend en charge l'envoi d'une partie d'une valeur et non pas de la valeur entière, décrite sous la forme d'une plage d'index d'octets. Vous pouvez fournir une plage d'index d'octets à la méthode send_blob() de BlobstoreDownloadHandler de deux façons. La première façon consiste à spécifier la plage à l'aide des arguments start et end :

            # Send the first 1,000 bytes of the value.
            self.send_blob(key, start=0, end=999)

Par défaut, BlobstoreDownloadHandler honore l'en-tête range de la requête. Si vous souhaitez bloquer l'utilisation de l'en-tête de plage d'origine, indiquez le paramètre use_range=False pour send_blob() :

            # Send the full value of the blob and
            # block the "range" header.
            self.send_blob(key, use_range=False)

La valeur de l'en-tête range est une plage d'octets HTTP standard. BlobstoreDownloadHandler utilise webob.byterange pour analyser cette valeur d'en-tête.

Exemple d'application complète

Dans l'exemple d'application suivant, l'URL principale de l'application charge le formulaire qui demande à l'utilisateur d'importer un fichier, et le gestionnaire d'importation appelle immédiatement le gestionnaire de téléchargement pour fournir les données. Cela permet de simplifier l'exemple d'application. En pratique, il est peu probable que vous utilisiez l'URL principale pour demander un transfert de données, ou encore que vous diffusiez un objet blob immédiatement après l'avoir importé.

from google.appengine.api import users
from google.appengine.ext import blobstore
from google.appengine.ext import ndb
from google.appengine.ext.webapp import blobstore_handlers
import webapp2


# This datastore model keeps track of which users uploaded which photos.
class UserPhoto(ndb.Model):
    user = ndb.StringProperty()
    blob_key = ndb.BlobKeyProperty()


class PhotoUploadFormHandler(webapp2.RequestHandler):
    def get(self):
        upload_url = blobstore.create_upload_url('/upload_photo')
        # To upload files to the blobstore, the request method must be "POST"
        # and enctype must be set to "multipart/form-data".
        self.response.out.write("""
<html><body>
<form action="{0}" method="POST" enctype="multipart/form-data">
  Upload File: <input type="file" name="file"><br>
  <input type="submit" name="submit" value="Submit">
</form>
</body></html>""".format(upload_url))


class PhotoUploadHandler(blobstore_handlers.BlobstoreUploadHandler):
    def post(self):
        upload = self.get_uploads()[0]
        user_photo = UserPhoto(
            user=users.get_current_user().user_id(),
            blob_key=upload.key())
        user_photo.put()

        self.redirect('/view_photo/%s' % upload.key())


class ViewPhotoHandler(blobstore_handlers.BlobstoreDownloadHandler):
    def get(self, photo_key):
        if not blobstore.get(photo_key):
            self.error(404)
        else:
            self.send_blob(photo_key)


app = webapp2.WSGIApplication([
    ('/', PhotoUploadFormHandler),
    ('/upload_photo', PhotoUploadHandler),
    ('/view_photo/([^/]+)?', ViewPhotoHandler),
], debug=True)