App Engine APIs für Cloud Storage verwenden

In diesem Dokument wird beschrieben, wie Daten mithilfe der App Engine-Clientbibliothek für Cloud Storage gespeichert und abgerufen werden. Außerdem sollten Sie eine App Engine-Anwendung wie in der Kurzanleitung für Java 8 in der App Engine-Standardumgebung beschrieben erstellen können. Es wird auch vorausgesetzt, dass Sie die grundlegenden Konzepte für die Verwendung von Cloud Storage in App Engine kennen.

App Engine-Clientbibliothek für Cloud Storage herunterladen

Sie können die Bibliothek mit beliebten Tools wie Apache Maven, Apache Ivy und Git oder auch manuell aus dem Maven-Repository herunterladen. Wählen Sie Ihre bevorzugte Methode aus:

Git

Wenn Sie Git installiert haben, können Sie das GitHub-Repository der Google Cloud Storage-Clientbibliothek wie folgt klonen:

git clone https://github.com/GoogleCloudPlatform/appengine-gcs-client.git

Maven

Maven-Nutzer nehmen Folgendes in die pom.xml-Datei ihrer Anwendung auf:

<dependency>
    <groupId>com.google.appengine.tools</groupId>
    <artifactId>appengine-gcs-client</artifactId>
    <version>0.8</version>
</dependency>

Ivy

Ivy-Nutzer sollten die folgenden Dateien in die Datei ivy.xml der Anwendung aufnehmen:

<dependency org="com.google.appengine.tools"
            name="appengine-gcs-client"
            rev="latest.integration" />

Manueller Download

Öffnen Sie das Maven-Repository der Bibliothek und laden Sie aktuelle Klassen-, Quell- und JavaDoc-JAR-Dateien herunter:

Außerdem müssen Sie die folgenden Abhängigkeiten herunterladen und in Ihre Anwendung aufnehmen:

ACLs und die App Engine-Clientbibliothek

Eine Anwendung, die die Clientbibliothek verwendet, kann die Bucket-ACL nicht ändern, jedoch eine ACL angeben, die den Zugriff auf die von ihr erstellten Objekte steuert. Die verfügbaren ACL-Einstellungen werden in der Dokumentation zur Klasse FcsFileOptions beschrieben.

Cloud Storage und Unterverzeichnisse

Mit der App Engine-Clientbibliothek für Cloud Storage können Sie beim Erstellen eines Objekts Trennzeichen für Unterverzeichnisse angeben, es gibt jedoch keine echten Unterverzeichnisse in Cloud Storage. In Cloud Storage ist ein Unterverzeichnis vielmehr ein Teil des Objektdateinamens.

Wenn beispielsweise ein Objekt somewhere/over/the/rainbow.mp3 erstellt wird, gehen Sie vermutlich davon aus, dass die Datei rainbow.mp3 im Unterverzeichnis somewhere/over/the/ gespeichert wird. Stattdessen wird damit aber als Objektname somewhere/over/the/rainbow.mp3 festgelegt.

App Engine-Clientbibliothek mit dem Entwicklungs-Anwendungsserver verwenden

Sie können die Clientbibliothek mit dem Entwicklungsserver verwenden. Da Cloud Storage jedoch nicht lokal emuliert wird, müssen alle Lese- und Schreibanfragen für Dateien über das Internet an einen tatsächlichen Cloud Storage-Bucket gesendet werden.

Führen Sie dev_appserver.py mit dem Flag --default_gcs_bucket_name [BUCKET_NAME] aus und ersetzen Sie [BUCKET_NAME] durch den Namen des von Ihnen verwendeten Cloud Storage-Buckets, um die Clientbibliothek mit dem Entwicklungs-Anwendungsserver zu verwenden.

Mit diesem Flag wird gesteuert, welcher Bucket zurückgegeben wird, wenn die Anwendung file.DefaultBucketName(ctx) aufruft.

In Google Cloud Storage lesen und schreiben

Erforderliche Importe

Das folgende Snippet zeigt die Importe, die Sie für den Cloud Storage-Zugriff über die Client-Bibliothek benötigen:

import com.google.appengine.tools.cloudstorage.GcsFileOptions;
import com.google.appengine.tools.cloudstorage.GcsFilename;
import com.google.appengine.tools.cloudstorage.GcsInputChannel;
import com.google.appengine.tools.cloudstorage.GcsOutputChannel;
import com.google.appengine.tools.cloudstorage.GcsService;
import com.google.appengine.tools.cloudstorage.GcsServiceFactory;
import com.google.appengine.tools.cloudstorage.RetryParams;

Cloud Storage-Bucket angeben

Das folgende Snippet zeigt eine Möglichkeit, wie der Nutzer beim Schreiben einer Datei in einen Bucket einen Bucket-Namen angeben kann:

function uploadFile() {
  var bucket = document.forms["putFile"]["bucket"].value;
  var filename = document.forms["putFile"]["fileName"].value;
  if (bucket == null || bucket == "" || filename == null || filename == "") {
    alert("Both Bucket and FileName are required");
    return false;
  } else {
    var postData = document.forms["putFile"]["content"].value;
    document.getElementById("content").value = null;

    var request = new XMLHttpRequest();
    request.open("POST", "/gcs/" + bucket + "/" + filename, false);
    request.setRequestHeader("Content-Type", "text/plain;charset=UTF-8");
    request.send(postData);
  }
}

Das Snippet stellt dem vom Nutzer bereitgestellten Bucket- und Dateinamen das erforderliche /gcs/ voran.

Schreibvorgänge in Cloud Storage

So schreiben Sie eine Datei in Cloud Storage:

@Override
public void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
  GcsFileOptions instance = GcsFileOptions.getDefaultInstance();
  GcsFilename fileName = getFileName(req);
  GcsOutputChannel outputChannel;
  outputChannel = gcsService.createOrReplace(fileName, instance);
  copy(req.getInputStream(), Channels.newOutputStream(outputChannel));
}

In diesem Beispiel wird eine neue Datei in Cloud Storage geschrieben. Wenn bereits eine Datei mit demselben Namen vorhanden ist, wird diese überschrieben. Dies ist nützlich, da Sie eine Datei nicht mehr ändern können, nachdem sie in Cloud Storage geschrieben wurde. Um die Datei zu ändern, müssen Sie Ihre Änderungen an einer Kopie der Datei vornehmen und dann die alte Datei überschreiben.

Lesevorgänge in Cloud Storage

So lesen Sie eine Datei aus Cloud Storage:

@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
  GcsFilename fileName = getFileName(req);
  if (SERVE_USING_BLOBSTORE_API) {
    BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
    BlobKey blobKey = blobstoreService.createGsBlobKey(
        "/gs/" + fileName.getBucketName() + "/" + fileName.getObjectName());
    blobstoreService.serve(blobKey, resp);
  } else {
    GcsInputChannel readChannel = gcsService.openPrefetchingReadChannel(fileName, 0, BUFFER_SIZE);
    copy(Channels.newInputStream(readChannel), resp.getOutputStream());
  }
}

Beachten Sie, dass in der Zeile, die gcsService.openPrefetchingReadChannel enthält, Prefetching verwendet wird. Damit werden die Daten zwischengespeichert und abgerufen, bevor sie benötigt werden, um ein Blockieren während des Leseaufrufs zu vermeiden.

Objekte in Cloud Storage ändern

Wenn Sie ein vorhandenes Objekt in Cloud Storage ändern möchten, schreiben Sie das geänderte Objekt mit demselben Dateinamen wie das Original.

Objekte aus Cloud Storage löschen

Mit der folgenden Prozedur wird gezeigt, wie Objekte in einer Anwendung gelöscht werden, die den vollständigen Cloud Storage-Objektnamen (Dateinamen) in einer Cloud SQL-Datenbank speichert:

final String bucket = "CLOUD-STORAGE-BUCKET"; // Cloud Storage bucket name
Map<String, String[]> userData = req.getParameterMap();

String[] image = userData.get("id"); // Grab the encoded image ID
String decodedId = new String(Base64.getUrlDecoder().decode(image[0])); // decode the image ID
int imageId = Integer.parseInt(decodedId);

// Grab the filename and build out a Cloud Storage filepath in preparation for deletion
try (PreparedStatement statementDeletePost = conn.prepareStatement(imageFilenameSql)) {
  statementDeletePost.setInt(1, imageId); // cast String to Int
  ResultSet rs = statementDeletePost.executeQuery(); // remove image record
  rs.next(); // move the cursor

  GcsFilename filename = new GcsFilename(bucket, rs.getString("filename"));
  if (gcsService.delete(filename)) {

    // Remove all records of image use in the blog
    // Use of foreign keys with cascading deletes will cause removal from blogpostImages table
    PreparedStatement statementDeleteImageRecord = conn.prepareStatement(deleteSql);
    statementDeleteImageRecord.setInt(1, imageId);
    statementDeleteImageRecord.executeUpdate();

    final String confirmation =
        "Image ID "
            + imageId
            + " has been deleted and record of its use in blog posts have been removed.";

    req.setAttribute("confirmation", confirmation);
    req.getRequestDispatcher("/confirm.jsp").forward(req, resp);
  } else {
    final String confirmation = "File marked for deletion does not exist.";

    req.setAttribute("confirmation", confirmation);
    req.getRequestDispatcher("/confirm.jsp").forward(req, resp);
  }
} catch (SQLException e) {
  throw new ServletException("SQL error", e);
}

Der obige Code decodiert die Base64-codierte Bild-ID und ruft den Dateinamen eines Bildes ab, das durch seine image_id in der images-Tabelle ermittelt wird. Der Dateiname wird mithilfe von GcsFilename in einen gültigen Cloud Storage-Dateinamen umgewandelt.

Die Datei wird dann mithilfe von gcsService.delete aus dem Bucket gelöscht. Abschließend wird der Datensatz zur Verwendung der Datei aus der Tabelle blogpostImage entfernt.

Nächste Schritte