Blobstore API für Java 8

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 Hochladen einer Datei über eine HTTP-Anfrage erstellt. Dazu wird dem Nutzer in der Regel in der Anwendung ein Formular mit einem entsprechenden Feld präsentiert. Nach dem Senden des Formulars erstellt Blobstore aus den Dateiinhalten ein Blob und gibt eine intransparente Referenz zurück. Diese wird als Blob-Schlüssel bezeichnet und 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 generiert damit die Aktions-URL des Formulars. Die Datei wird vom Browser des Nutzers über diese URL direkt in Blobstore hochgeladen. Blobstore speichert das Blob, fügt den Blob-Schlüssel in die Anfrage ein und übergibt ihn an einen Pfad in Ihrer 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. Diese Größe beträgt etwas weniger als 32 Megabyte, in Java dargestellt durch die Konstante com.google.appengine.api.blobstore.BlobstoreService.MAX_BLOB_FETCH_SIZE. Eine Anwendung kann keine Blobstore-Werte erstellen oder ändern. Das ist nur über Dateien möglich, 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. Ihre Anwendung legt blobstoreService.createUploadUrl() als Ziel (Aktion) für dieses Formular fest und übergibt die Funktion einem URL-Pfad eines Handlers in der Anwendung. Sendet der Nutzer das Formular ab, lädt der Browser die angegebenen Dateien direkt in Blobstore hoch. Der Blobstore schreibt die Anfrage des Nutzers um und speichert die hochgeladenen Dateidaten. Dabei ersetzt er die hochgeladenen Dateidaten mit einer oder mehreren übereinstimmenden BLOB-Schlüsseln und gibt dann die umgeschriebene Anfrage an den Handler weiter, dessen URL-Pfad Sie blobstoreService.createUploadUrl() zugewiesen 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. Weitere Informationen finden Sie unter der Klasse BlobstoreInputStream.

Blob hochladen

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

1. Upload-URL erstellen

Rufen Sie blobstoreService.createUploadUrl auf, um eine Upload-URL für das Formular zu erstellen, das durch den Nutzer ausgefüllt wird. Dabei wird der Anwendungspfad übergeben, der nach Durchführen des POST des Formulars geladen werden soll.

<body>
    <form action="<%= blobstoreService.createUploadUrl("/upload") %>" method="post" enctype="multipart/form-data">
        <input type="file" name="myFile">
        <input type="submit" value="Submit">
    </form>
</body>

Hinweis: So würde das Upload-Formular aussehen, wenn es als JSP erstellt worden wäre.

2. Uploadformular erstellen

Das Formular muss ein Feld für das Hochladen von Dateien 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. Die API erstellt auch den Informationseintrag für das Blob und speichert ihn im Datenspeicher. Außerdem übergibt sie die umgeschriebene Anfrage als Blob-Schlüssel unter dem angegebenen Pfad an die Anwendung.

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:

Im folgenden Code gibt getUploads eine Reihe von hochgeladenen Blobs zurück. Das Objekt Map ist eine Liste, in der die Namen der Upload-Felder den darin enthaltenen Blobs zugeordnet sind.

Map<String, List<BlobKey>> blobs = blobstoreService.getUploads(req);
List<BlobKey> blobKeys = blobs.get("myFile");

if (blobKeys == null || blobKeys.isEmpty()) {
    res.sendRedirect("/");
} else {
    res.sendRedirect("/serve?blob-key=" + blobKeys.get(0).getKeyString());
}

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

Um Blobs bereitzustellen, müssen Sie in der Anwendung einen Blob-Download-Handler als Pfad angeben. Dieser Handler sollte den BLOB-Schlüssel für den gewünschten BLOB an blobstoreService.serve(blobKey, res); weitergeben. In diesem Beispiel wird der Blob-Schlüssel als URL-Argument (req.getParameter('blob-key')) an den Download-Handler weitergegeben. In der Praxis kann der Download-Handler den Blob-Schlüssel gemäß Ihren Vorgaben abrufen, zum Beispiel durch eine andere Methode oder eine Nutzeraktion.

public void doGet(HttpServletRequest req, HttpServletResponse res)
    throws IOException {
        BlobKey blobKey = new BlobKey(req.getParameter("blob-key"));
        blobstoreService.serve(blobKey, res);

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

Blob-Bytebereiche

Statt der Bereitstellung des vollständigen Werts bei einer Antwort zu einer Anforderung wird bei Blobstore die Bereitstellung von Teilen eines großen Werts unterstützt. Fügen Sie den X-AppEngine-BlobRange-Header in die ausgehende Antwort ein, um einen Teilwert bereitzustellen. 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. Wenn der Bereich für den Wert nicht gültig ist, übergibt Blobstore den Statuscode 416 Requested Range Not Satisfiable.

Die Angabe mehrerer Bytebereiche (z. B. 100-199,200-299) in einer Anfrage wird vom Blobstore nicht unterstützt, egal, ob sie sich überlappen oder nicht.

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.

// file Upload.java

import java.io.IOException;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;

public class Upload extends HttpServlet {
    private BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();

    @Override
    public void doPost(HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {

        Map<String, List<BlobKey>> blobs = blobstoreService.getUploads(req);
        List<BlobKey> blobKeys = blobs.get("myFile");

        if (blobKeys == null || blobKeys.isEmpty()) {
            res.sendRedirect("/");
        } else {
            res.sendRedirect("/serve?blob-key=" + blobKeys.get(0).getKeyString());
        }
    }
}

// file Serve.java

import java.io.IOException;

import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.google.appengine.api.blobstore.BlobKey;
import com.google.appengine.api.blobstore.BlobstoreService;
import com.google.appengine.api.blobstore.BlobstoreServiceFactory;

public class Serve extends HttpServlet {
    private BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse res)
        throws IOException {
            BlobKey blobKey = new BlobKey(req.getParameter("blob-key"));
            blobstoreService.serve(blobKey, res);
        }
}

// file index.jsp

<%@ page import="com.google.appengine.api.blobstore.BlobstoreServiceFactory" %>
<%@ page import="com.google.appengine.api.blobstore.BlobstoreService" %>

<%
    BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
%>

<html>
    <head>
        <title>Upload Test</title>
    </head>
    <body>
        <form action="<%= blobstoreService.createUploadUrl("/upload") %>" method="post" enctype="multipart/form-data">
            <input type="text" name="foo">
            <input type="file" name="myFile">
            <input type="submit" value="Submit">
        </form>
    </body>
</html>

// web.xml

<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns="http://java.sun.com/xml/ns/javaee"
   xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
   http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">

  <servlet>
    <servlet-name>Upload</servlet-name>
    <servlet-class>Upload</servlet-class>
  </servlet>

  <servlet>
    <servlet-name>Serve</servlet-name>
    <servlet-class>Serve</servlet-class>
  </servlet>

  <servlet-mapping>
    <servlet-name>Upload</servlet-name>
    <url-pattern>/upload</url-pattern>
  </servlet-mapping>

  <servlet-mapping>
    <servlet-name>Serve</servlet-name>
    <url-pattern>/serve</url-pattern>
  </servlet-mapping>

</web-app>

Den Bildbearbeitungsdienst mit dem 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 gemäß der Google Cloud Storage-Dokumentation einen Bucket einrichten und diesen mit dem Dateinamen im Parameter BlobstoreService in der createUploadUrl angeben. Den Bucket-Namen legen Sie im Parameter UploadOptions fest. 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. Bei diesem Beispiel handelt es sich um einen Anfrage-Handler, dem der Bucket- und Objektname in einer Anfrage übergeben wird. Der Handler erstellt den Blobstore-Dienst und verwendet ihn, um anhand des Bucket- und Objektnamens einen Blob-Schlüssel für Google Cloud Storage zu erstellen:

BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
BlobKey blobKey = blobstoreService.createGsBlobKey(
    "/gs/" + fileName.getBucketName() + "/" + fileName.getObjectName());
blobstoreService.serve(blobKey, resp);

Kontingente und Limits

Der von Blobstore-Werten belegte Speicherplatz wird dem Kontingent Gespeicherte Daten (kostenpflichtig) angerechnet. Blob-Informationsentitäten im Datenspeicher werden den datenspeicherspezifischen Beschränkungen angerechnet. Beachten Sie, dass Cloud Storage ein kostenpflichtiger Dienst ist. Die Gebühren 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.