Blobstore Python API

Hinweis: Google Cloud Storage ist zum Speichern von Blob-Daten besser geeignet als Blobstore.

Die Blobstore API ermöglicht einer Anwendung, als Blobs bezeichnete Datenobjekte bereitzustellen, deren Größe die im Datastore-Dienst zulässige maximale Objektgröße bei Weitem überschreitet. Blobs sind hilfreich, um große Dateien bereitzustellen, z. B. Video- oder Bilddateien, und um Nutzern das Hochladen großer Datendateien zu ermöglichen. Blobs werden durch das Hochladen einer Datei über eine HTTP-Anfrage erstellt. Dazu wird dem Nutzer in der Anwendung ein Formular mit einem entsprechenden Feld präsentiert. Wenn das Formular gesendet wird, erstellt Blobstore aus den Dateiinhalten ein Blob und gibt eine intransparente Referenz zurück. Diese wird als Blob-Schlüssel bezeichnet und wird später zur Bereitstellung des Blobs verwendet. Die Anwendung kann als Antwort auf eine Nutzeranfrage den vollständigen Blob-Wert bereitstellen oder den Wert über eine dateiähnliche Streaming-Schnittstelle lesen.

Einführung in Blobstore

Google App Engine enthält den Blobstore-Dienst, mit dem Anwendungen sehr große Datenobjekte bereitstellen können. Die Größe ist nur durch die Datenmenge begrenzt, die über eine einzelne HTTP-Verbindung hoch- bzw. heruntergeladen werden kann. Diese Objekte heißen Blobstore-Werte oder Blobs. Ein Blobstore-Wert wird als Antwort eines Anfrage-Handlers bereitgestellt und als Upload über ein Webformular erstellt. Blob-Daten werden von Anwendungen nicht direkt erstellt. Stattdessen werden Blobs indirekt über ein Webformular oder eine andere POST-HTTP-Anfrage erstellt. Mithilfe der Blobstore API können Blobstore-Werte dem Nutzer bereitgestellt oder von der Anwendung in einem dateiähnlichen Stream abgerufen werden.

In der Anwendung wird ein Webformular mit einem Feld zum Dateiupload angezeigt, über das der Nutzer einen Blobstore-Wert hochladen kann. Die Anwendung ruft die Blobstore API auf und erzeugt die Aktions-URL des Formulars. Die Datei wird vom Browser des Nutzers über diese URL direkt in Blobstore geladen. Blobstore speichert das Blob, fügt den Blob-Schlüssel in die Anfrage ein und übergibt ihn an einen Pfad in der Anwendung. Ein Anfrage-Handler unter diesem Anwendungspfad kann das Formular dann weiter verarbeiten.

Zum Bereitstellen eines Blobs legt die Anwendung einen Header für die ausgehende Antwort fest und App Engine ersetzt die Antwort durch den Blob-Wert.

Nach ihrer Erstellung können Blobs nicht mehr geändert, aber gelöscht werden. Für jedes Blob wird im Datenspeicher ein Blob-Informationseintrag angelegt, der Details zum Blob enthält (z. B. Erstellungszeitpunkt und Inhaltstyp). Sie können den Blob-Schlüssel zum Abrufen von Blob-Informationseinträgen und zur Abfrage der enthaltenen Attribute verwenden.

Mithilfe eines API-Aufrufs kann eine Anwendung Teile eines Blobstore-Werts lesen. Ein Teil kann dabei maximal so groß sein wie ein API-Rückgabewert. Die Größe beträgt etwas weniger als 32 Megabyte, in Python durch die Konstante google.appengine.ext.blobstore.MAX_BLOB_FETCH_SIZE dargestellt. Eine Anwendung kann Blobstore-Werte nicht erstellen oder ändern. Das funktioniert nur über Dateien, die von Nutzern hochgeladen werden.

Blobstore verwenden

Anwendungen können mit Blobstore große Dateien, die von Nutzern hochgeladen werden, verarbeiten und bereitstellen. Sobald sie hochgeladen wurden, werden diese Dateien als Blobs bezeichnet. Anwendungen greifen nicht direkt auf Blobs zu. Sie verwenden dafür Blob-Informationsentitäten (durch die Klasse BlobInfo dargestellt) im Datenspeicher.

Der Nutzer erstellt ein Blob, indem er ein HTML-Formular mit einem oder mehreren Dateieingabefeldern sendet. Die Anwendung ermittelt durch Aufruf von create_upload_url() das Ziel (Aktion) für dieses Formular und übergibt der Funktion einen URL-Pfad eines Handlers in der Anwendung. Sendet der Nutzer das Formular ab, lädt der Browser die angegebenen Dateien direkt in Blobstore hoch. Blobstore schreibt den Nutzer-Request um, speichert die hochgeladenen Dateidaten und ersetzt sie durch einen oder mehrere Blob-Schlüssel. Anschließend wird der geänderte Request an den Handler in dem URL-Pfad übergeben, den Sie für create_upload_url() angegeben haben. Der Handler kann auf Grundlage des Blob-Schlüssels weitere Verarbeitungsschritte ausführen.

Die Anwendung kann Teile eines Blobstore-Werts mithilfe einer dateiähnlichen Streamingschnittstelle lesen. Details finden Sie in den Informationen zur Klasse BlobReader.

Blob hochladen

Gehen Sie beim Erstellen und Hochladen eines Blobs folgendermaßen vor:

1. Upload-URL erstellen

Der Aufruf von blobstore.create_upload_url() erstellt eine Upload-URL für das Formular, das der Nutzer ausfüllt. Dabei wird der Anwendungspfad übergeben, der am Ende des POST-Teils des Formulars geladen werden soll.

upload_url = blobstore.create_upload_url('/upload_photo')

Es gibt eine asynchrone Version: create_upload_url_async(). Mit dieser Version kann der Anwendungscode weiterhin ausgeführt werden, während Blobstore die Upload-URL generiert.

2. Uploadformular erstellen

Das Formular muss ein Feld für den Dateiupload enthalten und der enctype des Formulars muss auf multipart/form-data festgelegt sein. Wenn der Nutzer das Formular absendet, wird der POST von der Blobstore API verarbeitet, die das Blob erstellt. Außerdem erstellt die API einen Informationseintrag für das Blob und speichert diesen im Datenspeicher. Der umgeschriebene Request wird als Blob-Schlüssel unter dem angegebenen Pfad an die Anwendung übergeben.

        # 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))

3. Upload-Handler implementieren

In diesem Handler können Sie den Blob-Schlüssel zusammen mit dem restlichen Datenmodell der Anwendung speichern. Auf den Blob-Schlüssel selbst lässt sich über die Blob-Informationsentität im Datenspeicher zugreifen. Beachten Sie, dass das Blob bereits gespeichert und die Blob-Informationen dem Datenspeicher hinzugefügt wurden, nachdem der Nutzer das Formular gesendet hat und der Handler aufgerufen wurde. Wenn die Anwendung das Blob nicht mehr benötigt, sollten Sie es sofort löschen, damit es nicht verwaist:

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())

Das Web-App-Framework stellt die Upload-Handler-Klasse blobstore_handlers.BlobstoreUploadHandler bereit, um Sie beim Parsen der Formulardaten zu unterstützen. Weitere Informationen finden Sie in der Referenz zu BlobstoreUploadHandler.

Wenn Blobstore den Nutzer-Request neu schreibt, wird der Text in den MIME-Teilen der hochgeladenen Dateien gelöscht und der Blob-Schlüssel wird als Header für die MIME-Teile hinzugefügt. Alle anderen Formularfelder und Teile werden beibehalten und an den Upload-Handler weitergeleitet. Wenn Sie keinen Inhaltstyp angeben, versucht Blobstore, ihn von der Dateierweiterung abzuleiten. Kann kein Inhaltstyp bestimmt werden, wird dem neu erstellten Blob der Typ application/octet-stream zugewiesen.

Blob bereitstellen

Wenn Sie Blobs bereitstellen möchten, müssen Sie in der Anwendung einen Blob-Download-Handler als Pfad angeben. Zum Bereitstellen eines Blobs legt die Anwendung einen Header für die ausgehende Antwort fest. Im folgenden Beispiel wird das webapp-Framework verwendet. Bei Verwendung von webapp übergibt der Handler den Blob-Schlüssel für das gewünschte Blob an self.send_blob(). In diesem Beispiel wird der Blob-Schlüssel als Teil der URL an den Download-Handler übergeben. In der Praxis kann der Download-Handler den Blob-Schlüssel anhand Ihrer Vorgaben abrufen, zum Beispiel durch eine andere Methode oder Nutzeraktion.

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)

Das Web-App-Framework stellt die Download-Handler-Klasse blobstore_handlers.BlobstoreDownloadHandler bereit, um Sie beim Parsen der Formulardaten zu unterstützen. Weitere Informationen finden Sie in der Referenz zu BlobstoreDownloadHandler.

Blobs können von jeder Anwendungs-URL bereitgestellt werden. Zum Bereitstellen eines Blobs in Ihrer Anwendung fügen Sie der Antwort einen speziellen Header hinzu, der den Blob-Schlüssel enthält. App Engine ersetzt den Antworttext durch den Blob-Inhalt.

Blob-Bytebereiche

Anstelle des vollständigen Werts kann mit Blobstore nur ein Teil eines großen Werts als Antwort auf eine Anfrage bereitgestellt werden. Zu diesem Zweck wird der Header X-AppEngine-BlobRange in die ausgehende Antwort eingefügt. Der Wert dieses Headers ist ein normaler HTTP-Bytebereich. Die Bytenummerierung ist nullbasiert. Wenn X-AppEngine-BlobRange leer ist, ignoriert die API den Bereichsheader und stellt das vollständige Blob bereit. Einige Beispielbereiche:

  • 0-499 stellt die ersten 500 Byte des Werts bereit (Byte 0 bis einschließlich Byte 499).
  • 500-999 stellt 500 Byte ab Byte 501 bereit.
  • 500- stellt alle Byte ab Byte 501 bis zum Ende des Werts bereit.
  • -500 stellt die letzten 500 Byte des Werts bereit.

Wenn der Bytebereich für den Blobstore-Wert gültig ist, sendet Blobstore den Statuscode 206 Partial Content und den angeforderten Bytebereich an den Client. Ist der Bereich für den Wert ungültig, sendet Blobstore 416 Requested Range Not Satisfiable.

Die Angabe mehrerer Bytebereiche (z. B. 100-199,200-299) in einem Request wird von Blobstore nicht unterstützt, unabhängig davon, ob sie sich überlappen.

Die Klasse webapp.blobstore_handlers.BlobstoreDownloadHandler enthält Features zum Festlegen dieses Headers unter Verwendung der bereitgestellten Byte-Indexe und zum automatischen Ableiten des Bytebereichs aus einem range-Header, der vom Nutzer angegeben wurde.

Komplette Beispielanwendung

In der folgenden Beispielanwendung wird über die Haupt-URL der Anwendung das Formular geladen, das den Nutzer zum Hochladen einer Datei auffordert. Der Upload-Handler ruft sofort den Download-Handler auf, um die Daten bereitzustellen. Das vereinfacht die Beispielanwendung. In der Praxis würden Sie wahrscheinlich nicht die Haupt-URL verwenden, um das Hochladen von Daten anzufordern. Und Sie würden ein gerade hochgeladenes Blob auch nicht sofort bereitstellen.

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)

Bildbearbeitungsdienst mit Blobstore verwenden

Der Bildbearbeitungsdienst kann einen Blobstore-Wert als Transformationsquelle nutzen. Das Quellbild kann die maximale Größe für einen Blobstore-Wert annehmen. Da der Bildbearbeitungsdienst das transformierte Bild an die Anwendung zurückgibt, muss es kleiner als 32 Megabyte sein. Dies ist nützlich, um von großen Fotos, die von Nutzern hochgeladen wurden, Miniaturansichten zu erstellen.

Informationen zur Verwendung des Bildbearbeitungsdienstes mit Blobstore-Werten finden Sie in der zugehörigen Dokumentation.

Blobstore API mit Google Cloud Storage verwenden

Die Blobstore API ermöglicht das Speichern von Blobs in Cloud Storage statt in Blobstore. Dazu müssen Sie einen Bucket einrichten und zusammen mit dem Dateinamen im Parameter gs_bucket_name der blobstore.blobstore.create_upload_url angeben. Folgen Sie hierzu der Anleitung in der Dokumentation zu Google Cloud Storage. Im Upload-Handler müssen Sie die zurückgegebenen FileInfo-Metadaten verarbeiten und den Google Cloud Storage-Dateinamen, der zum späteren Abrufen des Blobs benötigt wird, explizit speichern.

Cloud Storage-Objekte lassen sich auch mithilfe der Blobstore API bereitstellen. Die folgenden Code-Snippets zeigen, wie das geht.

"""A sample app that operates on GCS files with blobstore API."""

import cloudstorage
from google.appengine.api import app_identity
from google.appengine.ext import blobstore
from google.appengine.ext.webapp import blobstore_handlers
import webapp2

# This handler creates a file in Cloud Storage using the cloudstorage
# client library and then reads the data back using the Blobstore API.
class CreateAndReadFileHandler(webapp2.RequestHandler):
    def get(self):
        # Get the default Cloud Storage Bucket name and create a file name for
        # the object in Cloud Storage.
        bucket = app_identity.get_default_gcs_bucket_name()

        # Cloud Storage file names are in the format /bucket/object.
        filename = '/{}/blobstore_demo'.format(bucket)

        # Create a file in Google Cloud Storage and write something to it.
        with cloudstorage.open(filename, 'w') as filehandle:
            filehandle.write('abcde\n')

        # In order to read the contents of the file using the Blobstore API,
        # you must create a blob_key from the Cloud Storage file name.
        # Blobstore expects the filename to be in the format of:
        # /gs/bucket/object
        blobstore_filename = '/gs{}'.format(filename)
        blob_key = blobstore.create_gs_key(blobstore_filename)

        # Read the file's contents using the Blobstore API.
        # The last two parameters specify the start and end index of bytes we
        # want to read.
        data = blobstore.fetch_data(blob_key, 0, 6)

        # Write the contents to the response.
        self.response.headers['Content-Type'] = 'text/plain'
        self.response.write(data)

        # Delete the file from Google Cloud Storage using the blob_key.
        blobstore.delete(blob_key)

# This handler creates a file in Cloud Storage using the cloudstorage
# client library and then serves the file back using the Blobstore API.
class CreateAndServeFileHandler(blobstore_handlers.BlobstoreDownloadHandler):

    def get(self):
        # Get the default Cloud Storage Bucket name and create a file name for
        # the object in Cloud Storage.
        bucket = app_identity.get_default_gcs_bucket_name()

        # Cloud Storage file names are in the format /bucket/object.
        filename = '/{}/blobstore_serving_demo'.format(bucket)

        # Create a file in Google Cloud Storage and write something to it.
        with cloudstorage.open(filename, 'w') as filehandle:
            filehandle.write('abcde\n')

        # In order to read the contents of the file using the Blobstore API,
        # you must create a blob_key from the Cloud Storage file name.
        # Blobstore expects the filename to be in the format of:
        # /gs/bucket/object
        blobstore_filename = '/gs{}'.format(filename)
        blob_key = blobstore.create_gs_key(blobstore_filename)

        # BlobstoreDownloadHandler serves the file from Google Cloud Storage to
        # your computer using blob_key.
        self.send_blob(blob_key)

app = webapp2.WSGIApplication([
    ('/', CreateAndReadFileHandler),
    ('/blobstore/read', CreateAndReadFileHandler),
    ('/blobstore/serve', CreateAndServeFileHandler)], debug=True)

BlobReader verwenden

Eine Anwendung kann über eine dem Python-Objekt file ähnliche Schnittstelle Daten aus Blobstore-Werten lesen. Diese Schnittstelle kann an jeder Byteposition einen Wert lesen. Sie verwendet mehrere Dienstaufrufe und Zwischenspeicherungen, damit die Anwendung trotz der Größenbeschränkung einer einzelnen Dienstaufrufantwort auf die volle Größe des Werts zugreifen kann.

Die Klasse BlobReader kann einen von drei Werten als Argument für ihren Konstruktor annehmen:

Das Objekt implementiert die bekannten Dateimethoden zum Lesen des Werts. Die Anwendung kann den Blobstore-Wert nicht ändern. Dateimethoden zum Schreiben sind nicht implementiert.

# Instantiate a BlobReader for a given Blobstore blob_key.
blob_reader = blobstore.BlobReader(blob_key)

# Instantiate a BlobReader for a given Blobstore blob_key, setting the
# buffer size to 1 MB.
blob_reader = blobstore.BlobReader(blob_key, buffer_size=1048576)

# Instantiate a BlobReader for a given Blobstore blob_key, setting the
# initial read position.
blob_reader = blobstore.BlobReader(blob_key, position=0)

# Read the entire value into memory. This may take a while depending
# on the size of the value and the size of the read buffer, and is not
# recommended for large values.
blob_reader_data = blob_reader.read()

# Write the contents to the response.
self.response.headers['Content-Type'] = 'text/plain'
self.response.write(blob_reader_data)

# Set the read position back to 0, then read and write 3 bytes.
blob_reader.seek(0)
blob_reader_data = blob_reader.read(3)
self.response.write(blob_reader_data)
self.response.write('\n')

# Set the read position back to 0, then read and write one line (up to
# and including a '\n' character) at a time.
blob_reader.seek(0)
for line in blob_reader:
    self.response.write(line)

Asynchrone Requests ausführen

Eine Anwendung kann bestimmte Blobstore-Funktionen aufrufen, die im Hintergrund ausgeführt werden. Blobstore führt den Request aus, während die Anwendung andere Aufgaben erledigt. Zum Ausführen des Requests ruft die Anwendung eine asynchrone Funktion auf. Die Funktion gibt umgehend ein RPC-Objekt zurück, das die Anfrage darstellt. Wenn die Anwendung das Ergebnis der Anfrage benötigt, ruft sie die Methode get_result() des RPC-Objekts auf.

Wenn die Anwendung get_result() aufruft und der Dienst den Request noch nicht abgeschlossen hat, wartet die Methode, bis der Request abgeschlossen ist (bzw. die Frist erreicht wurde oder ein Fehler auftritt). Die Methode gibt das Ergebnisobjekt zurück oder löst eine Ausnahme aus, wenn beim Ausführen des Requests ein Fehler aufgetreten ist. Beispiel: Dieses Code-Snippet

upload_url = blobstore.create_upload_url('/upload')
slow_operation()
self.response.out.write("""<form action="%s" method="POST"
                           enctype="multipart/form-data">""" % upload_url)

wird zu

upload_url_rpc = blobstore.create_upload_url_async('/upload')
slow_operation()
upload_url = upload_url_rpc.get_result()
self.response.out.write("""<form action="%s" method="POST"
                           enctype="multipart/form-data">""" % upload_url)

In diesem Beispiel führt die Anwendung den Code slow_operation() zur selben Zeit aus, zu der Blobstore die Upload-URL generiert.

Kontingente und Beschränkungen

Der von Blobstore-Werten belegte Speicherplatz wird dem Kontingent Gespeicherte Daten (kostenpflichtig) angerechnet. Blob-Informationsentitäten im Datenspeicher werden den datenspeicherspezifischen Beschränkungen angerechnet. Google Cloud Storage ist ein zahlungspflichtiger nutzungsbasierter Dienst. Die anfallenden Kosten richten sich nach der Preisübersicht für Cloud Storage.

Weitere Informationen zu den systemweiten Sicherheitskontingenten finden Sie unter Kontingente.

Neben den systemweiten Sicherheitskontingenten gelten speziell für Blobstore folgende Beschränkungen:

  • Mit einem einzelnen API-Aufruf kann eine Anwendung maximal 32 Megabyte Blobstore-Daten lesen.
  • Mit einem einzigen Formular-POST können maximal 500 Dateien hochgeladen werden.
Hat Ihnen diese Seite weitergeholfen? Teilen Sie uns Ihr Feedback mit:

Feedback geben zu...

App Engine-Standardumgebung für Python 2