Cloud Storage での App Engine API の使用

このドキュメントでは、Cloud Storage 用 App Engine クライアント ライブラリを使用してデータを保存、取得する方法を説明します。前提として、Java 8 App Engine スタンダード環境のクイックスタートで説明されている、App Engine アプリケーションの作成方法を理解している必要があります。また、App Engine 内での Cloud Storage の使用に関する基本コンセプトを理解していることも前提となります。

Cloud Storage 用 App Engine クライアント ライブラリをダウンロードする

このライブラリは、Apache MavenApache IvyGit などのよく使われるツールを使用してダウンロードできます。あるいは、Maven リポジトリから手動でダウンロードすることもできます。必要に応じていずれかの方法を選択します。

Git

Git がインストールされている場合、以下のように Google Cloud Storage クライアント ライブラリの GitHub リポジトリのクローンを作成できます。

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

Maven

Maven ユーザーはアプリケーションの pom.xml ファイルに以下を含める必要があります。

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

Ivy

Ivy ユーザーはアプリケーションの ivy.xml ファイルに以下を含める必要があります。

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

手動ダウンロード

ライブラリの Maven リポジトリにアクセスして最近のクラス、ソース、JavaDoc JAR ファイルをダウンロードします。

また、以下の依存関係をダウンロードしてアプリケーションに含める必要もあります。

ACL と App Engine クライアント ライブラリ

クライアント ライブラリを使用するアプリではバケット ACL を変更できませんが、作成するオブジェクトへのアクセスを制御する ACL を指定することはできます。利用可能な ACL 設定については、FcsFileOptions クラスのドキュメントをご覧ください。

Cloud Storage とサブディレクトリ

Cloud Storage 用 App Engine クライアント ライブラリでは、オブジェクトの作成時にサブディレクトリの区切り文字を指定できますが、実際には Cloud Storage 内にサブディレクトリはありません。Cloud Storage では、オブジェクトのファイル名の一部がサブディレクトリに該当します。

たとえば、オブジェクト somewhere/over/the/rainbow.mp3 を作成する場合、そのファイル rainbow.mp3 がサブディレクトリ somewhere/over/the/ に格納されるのではなく、オブジェクト名が somewhere/over/the/rainbow.mp3 に設定されます。

開発用アプリサーバーで App Engine クライアント ライブラリを使用する

このクライアント ライブラリは開発用サーバーで使用できます。ただし、Cloud Storage のローカル エミュレーションは行われないため、ファイルの読み取りと書き込みのすべてのリクエストは、インターネット経由で実際の Cloud Storage バケットに送信する必要があります。

開発用アプリサーバーでこのクライアント ライブラリを使用するには、--default_gcs_bucket_name [BUCKET_NAME] フラグを指定して dev_appserver.py を実行します。[BUCKET_NAME] の部分は、使用している Cloud Storage バケットの名前で置き換えます。

このフラグは、アプリケーションが file.DefaultBucketName(ctx) を呼び出したときに返されるバケットを制御します。

Google Cloud Storage の読み取りと書き込み

必要なインポート

次のスニペットに、クライアント ライブラリ経由で Cloud Storage にアクセスするために必要なインポートを示します。

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 バケットを指定する

次のスニペットに、バケットにファイルを書き込む際、バケット名をユーザーに指定させる方法の 1 つを示します。

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);
  }
}

このスニペットは、必要な /gcs/ をユーザーの入力したバケット名とファイル名の先頭に追加します。

Cloud Storage に書き込む

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));
}

このサンプルは新しいファイルを Cloud Storage に書き込みます。同じ名前のファイルがすでに存在する場合は、それを上書きします。これは、ファイルを Cloud Storage に書き込むとそれを変更できないため便利です。ファイルを変更するには、ファイルのコピーに変更を加えてから、古いファイルを上書きする必要があります。

Cloud Storage から読み取る

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

gcsService.openPrefetchingReadChannel が含まれる行では、プリフェッチが使用されていることに注意します。これは、読み取り呼び出しに対するブロックを回避するために、データをメモリにバッファして、必要になる前にそのデータをプリフェッチするためです。

Cloud Storage でのオブジェクトの変更

Cloud Storage 内の既存のオブジェクトを変更するには、オブジェクトを変更する際に元のファイル名と同じ名前で書き込みます。

Cloud Storage からのオブジェクトの削除

次の手順は、Cloud SQL データベースに Cloud Storage オブジェクトの完全名(ファイル名)を保存するアプリでオブジェクトを削除する方法を示しています。

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);
}

上記のコードでは、Base64 エンコードされた画像 ID をデコードし、image_id で識別される画像のファイル名を images テーブルから取得しています。ファイル名は GcsFilename によって有効な Cloud Storage ファイル名に変換されます。

ファイルは gcsService.delete によってバケットから削除されます。最後に blogpostImage テーブルからそのファイルの利用状況のレコードが削除されます。

次のステップ

このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...

Java 8 の App Engine スタンダード環境