Blobstore API を使用すると、blob と呼ばれるデータ オブジェクトをアプリケーションで提供できるようになります。blob は、Datastore サービスのオブジェクトに許可されているサイズよりはるかに大きいサイズのオブジェクトです。blob は、動画や画像などのサイズの大きいファイルを提供する場合や、ユーザーがサイズの大きいデータファイルをアップロードする場合に便利です。blob は HTTP リクエストでファイルをアップロードすることによって作成されます。通常アプリケーションでこの処理を行うには、ファイル アップロード用のフィールドを含むフォームをユーザーに提示します。フォームが送信されると、Blobstore によってファイルの内容から blob が作成され、blob への不透明な参照(blob キー)が返されます。このキーは後で blob を提供するときに使用できます。アプリケーションは、ユーザー リクエストに応じて完全な blob 値を提供でき、あるいはファイルに似たストリーミング インターフェースを使用して、値を直接読み取ることができます。
Blobstore を導入する
Google App Engine には Blobstore サービスが含まれています。このサービスを利用すると、アプリケーションでデータ オブジェクトを提供できます。制限は 1 回の HTTP 接続でアップロードまたはダウンロードできるデータ量のみです。これらのオブジェクトを Blobstore 値または blob と呼びます。Blobstore 値はリクエスト ハンドラからのレスポンスとして提供され、ウェブフォームを通じたアップロード データとして作成されます。blob データは、アプリケーションで直接作成されるのではなく、送信されたウェブフォームや他の HTTP POST
リクエストによって間接的に作成されます。Blobstore API を使用すると、Blobstore 値をユーザーに送信することや、ファイルに似たストリームでアプリケーションから Blobstore 値にアクセスできます。
アプリケーションで Blobstore 値のアップロードをユーザーに促すには、ファイル アップロード用のフィールドを含むウェブフォームを提示します。アプリケーションは、Blobstore API を呼び出してフォームのアクション URL を生成します。ユーザーのブラウザは、生成された URL を通じてファイルを Blobstore に直接アップロードします。次に、Blobstore は blob を保存し、blob キーが含まれるようにリクエストを書き換えて、アプリケーションのパスに渡します。そのパスにはアプリケーションのリクエスト ハンドラがあり、そこで追加のフォーム処理を行うことができます。
blob を提供するには、アプリケーションで送信レスポンスにヘッダーを設定します。このレスポンスは App Engine で blob 値に置き換えられます。
作成した blob は変更できませんが、削除することはできます。各 blob には対応する blob 情報レコードがあり、データストアに保存されています。blob 情報レコードを参照すると、作成日時やコンテンツ タイプなどの blob の詳細がわかります。blob キーを使用して blob 情報レコードをフェッチし、そのプロパティへのクエリができます。
アプリケーションでは API を呼び出して Blobstore 値を読み取れますが、一度に読み取れるサイズは制限されています。制限サイズは API の戻り値の最大サイズで 32 MB より少し小さい値です。Java では、これは定数 com.google.appengine.api.blobstore.BlobstoreService.MAX_BLOB_FETCH_SIZE
で表されます。アプリケーションでは、ファイルをアップロードするという方法以外で Blobstore 値を作成または変更できません。
Blobstore を使用する
Blobstore を使用すると、ユーザーからアップロードされるサイズの大きなファイルをアプリケーションで受け取ることや提供することができます。アップロードされたファイルは blob と呼ばれます。アプリケーションは blob に直接アクセスするのではなく、データストアの blob 情報エンティティ(BlobInfo
クラスで表されます)を介して blob を使用します。
ユーザーは 1 つ以上のファイル入力フィールドを含む HTML フォームを送信して blob を作成します。アプリケーションは blobstoreService.createUploadUrl()
をこのフォームの送信先(アクション)として設定し、この関数にアプリケーションのハンドラの URL パスを渡します。ユーザーがフォームを送信すると、指定されたファイルがユーザーのブラウザから Blobstore に直接アップロードされます。Blobstore はユーザーのリクエストを書き換え、アップロードされたファイルデータを保存します。その際、アップロードされたファイルデータを 1 つ以上の対応する blob キーで置き換えます。その後、書き換えたリクエストを、blobstoreService.createUploadUrl()
に指定された URL パスにあるハンドラに渡します。このハンドラは、blob キーに基づいて追加の処理を行えます。
アプリケーションは、ファイルに似たストリーミング インターフェースを使用して、Blobstore 値の一部を読み取ることができます。BlobstoreInputStream
クラスをご覧ください。
blob をアップロードする
blob を作成してアップロードする手順は次のとおりです。
1. アップロード URL を作成する
blobstoreService.createUploadUrl
を呼び出して、ユーザーが記入するフォームのアップロード URL を作成し、そのフォームの POST
が完了したときに読み込むアプリケーション パスを渡します。
<body>
<form action="<%= blobstoreService.createUploadUrl("/upload") %>" method="post" enctype="multipart/form-data">
<input type="file" name="myFile">
<input type="submit" value="Submit">
</form>
</body>
このコードは、アップロード フォームを JSP として作成した場合の例です。
2. アップロード フォームを作成する
フォームにはファイル アップロード用のフィールドを含め、フォームの enctype
を multipart/form-data
と設定する必要があります。ユーザーがフォームを送信すると、Blobstore API が POST
を処理し、blob を作成します。この API はさらに、blob の情報レコードを作成してデータストアに保存し、指定されたパスにあるアプリケーションに、blob キーに書き換えたリクエストを渡します。
3. アップロード ハンドラを実装する
このハンドラでは、blob キーとともにアプリケーションのデータモデルの残りの部分を保存できます。blob キー自体には、データストアの blob 情報エンティティから引き続きアクセスできます。ユーザーがフォームを送信し、ハンドラが呼び出された時点で、blob はすでに保存され、blob 情報がデータストアに追加されていることに注意してください。アプリケーションに blob を保持しない場合は、すぐに blob を削除して blob が孤立しないようにしてください。
次のコードは、getUploads
がアップロード済みの一連の blob を返すようにするものです。Map
オブジェクトは、アップロード フィールドの名前をそのフィールドに含まれる blob に関連付けたリストです。
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());
}
Blobstore がユーザーのリクエストを書き換えると、アップロードされたファイルの MIME パーツの本文が空になり、MIME パーツのヘッダーとして blob キーが追加されます。他のすべてのフォーム フィールドとパーツは保持され、アップロード ハンドラに渡されます。コンテンツ タイプを指定していない場合、Blobstore はファイル拡張子から推定を試みます。コンテンツ タイプが判断できない場合、新しく作成される blob にはコンテンツ タイプ application/octet-stream
が割り当てられます。
blob を提供する
blob を提供するには、blob のダウンロード ハンドラをパスとしてアプリケーションに含める必要があります。このハンドラから、該当する blob の blob キーを blobstoreService.serve(blobKey, res);
に渡す必要があります。次の例では、blob キーは、URL 引数 (req.getParameter('blob-key'))
としてダウンロード ハンドラに渡されています。実際のダウンロード ハンドラでは、他のメソッドやユーザーの操作など任意の手段で blob キーを取得できます。
public void doGet(HttpServletRequest req, HttpServletResponse res)
throws IOException {
BlobKey blobKey = new BlobKey(req.getParameter("blob-key"));
blobstoreService.serve(blobKey, res);
blob は、任意のアプリケーション URL から提供できます。アプリケーションで blob を提供するには、blob キーを含むレスポンスに特別なヘッダーを配置します。App Engine はレスポンスの本文を blob のコンテンツに置き換えます。
blob バイトの範囲
Blobstore は、リクエストへのレスポンスとして、サイズの大きな値全体ではなく値の一部の提供をサポートしています。値の一部を提供するには、送信レスポンスに X-AppEngine-BlobRange
ヘッダーを含めます。その値は標準的な HTTP バイト範囲とします。バイト番号は 0 から始まります。X-AppEngine-BlobRange
を空にすると、API は範囲ヘッダーを無視して blob 全体を提供します。範囲の例を以下に示します。
0-499
: 値の最初の 500 バイト(0~499 バイト目まで)を提供します。500-999
: 501 バイト目から始まる 500 バイトを提供します。500-
: 501 バイト目から値の最後までのすべてのバイトを提供します。-500
: 値の最後の 500 バイトを提供します。
Blobstore 値に対して有効なバイト範囲が指定されている場合、Blobstore は 206
Partial
Content
ステータス コードとリクエストされたバイト範囲をクライアントに送信します。範囲が有効でなければ、Blobstore は 416
Requested
Range
Not
Satisfiable
を送信します。
Blobstore では、重複の有無にかかわらず 1 回のリクエストでの複数のバイト範囲の指定(100-199,200-299
など)はサポートしていません。
サンプル アプリケーションを完成させる
以下のサンプル アプリケーションでは、アプリケーションのメイン URL でユーザーにファイルのアップロードを求めるフォームを読み込みます。アップロード ハンドラはすぐにダウンロード ハンドラを呼び出し、データを提供します。これは、サンプル アプリケーションを簡略化するためのものです。実際にはアップロード データのリクエストにメイン URL を使用したり、アップロードされた blob をすぐに提供したりしなくてもかまいません。
// 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>
Blobstore で Images サービスを使用する
Images サービスでは、変換のソースとして Blobstore 値を使用できます。Blobstore 値の最大サイズと同サイズのソース画像を使用できます。Images サービスで変換後の画像がアプリケーションに返されるので、そのサイズは 32 MB より小さくなければなりません。これは、ユーザーがアップロードしたサイズの大きな写真のサムネイル画像を作成する場合に便利です。
Images サービスで Blobstore 値を使用する方法については、Images サービスのドキュメントをご覧ください。
Google Cloud Storage で Blobstore API を使用する
Blobstore API を使用して Blobstore ではなく Cloud Storage に blob を保存できます。Google Cloud Storage のドキュメントの説明に従ってバケットを設定して、そのバケットとファイル名を BlobstoreService
createUploadUrl で指定し、バケット名を UploadOptions
パラメータで指定する必要があります。アップロード ハンドラでは、返された FileInfo メタデータを処理し、後で blob を取得する際に必要となる Google Cloud Storage ファイル名を明示的に保存する必要があります。
Blobstore API を使用して Cloud Storage オブジェクトを提供することもできます。
以下のコード スニペットにその方法を示しています。このサンプルは、リクエストのバケット名とオブジェクト名を取得するリクエスト ハンドラの一部です。Blobstore サービスを作成し、そのサービスを使用して Google Cloud Storage の blob キーを作成します。その際、指定されたバケットとオブジェクト名を使用します。
BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
BlobKey blobKey = blobstoreService.createGsBlobKey(
"/gs/" + fileName.getBucketName() + "/" + fileName.getObjectName());
blobstoreService.serve(blobKey, resp);
割り当てと上限
Blobstore 値に使用する領域は、保存データ(課金対象)の割り当て量の計算対象になります。データストアの blob 情報エンティティはデータストア関連の制限に対してカウントされます。Google Cloud Storage は従量課金サービスであり、Cloud Storage の料金表に従って課金されることに注意してください。
システム全体の安全上の割り当て量について詳しくは、割り当てをご覧ください。
Blobstore の使用については特に、システム全体の安全上の割り当て量に加えて次の制限が適用されます。
- アプリケーションからの API 呼び出し 1 回で読み取ることのできる Blobstore データの最大サイズは 32 MB です。
- 1 つのフォームの POST でアップロードできるファイルの最大数は 500 です。