以前のバンドル サービス用の Blobstore API の概要

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 情報レコードをフェッチし、そのプロパティへのクエリができます。

Blobstore を使用する

Blobstore を使用すると、ユーザーからアップロードされるサイズの大きなファイルをアプリケーションで受け取ることや提供することができます。アップロードされたファイルは blob と呼ばれます。アプリケーションがファイル名を使用して直接 blob にアクセスすることはありません。代わりに、アプリケーションは appengine.BlobKey 型を通じて blob を参照します。

ユーザーは 1 つ以上のファイル入力フィールドを含む HTML フォームを送信して blob を作成します。アプリケーションは blobstore.UploadURL をこのフォームの送信先(アクション)として設定し、この関数にアプリケーションのハンドラの URL パスを渡します。ユーザーがフォームを送信すると、指定されたファイルがユーザーのブラウザから Blobstore に直接アップロードされます。Blobstore はユーザーのリクエストを書き換え、アップロードされたファイルデータを保存します。その際、アップロードされたファイルデータを 1 つ以上の対応する blob キーで置き換えます。その後、書き換えたリクエストを、blobstore.UploadURL に指定された URL パスにあるハンドラに渡します。このハンドラは、blob キーに基づいて追加の処理を行えます。最後に、ハンドラはヘッダーのみのリダイレクト応答(301、302、または 303)を返す必要があります。ブラウザは通常、blob アップロードのステータスを示す別のページにリダイレクトされます。

アプリケーションは、blobstore.Reader を使用して Blobstore 値の一部を読み取ることができます。

blob をアップロードする

blob を作成してアップロードする手順は次のとおりです。

1. アップロード URL を作成する

blobstore.UploadURL を呼び出して、ユーザーが記入するフォームのアップロード URL を作成し、そのフォームの POST が完了したときに読み込むアプリケーション パスを渡します。

ctx := appengine.NewContext(r)
uploadURL, err := blobstore.UploadURL(ctx, "/upload", nil)
if err != nil {
	serveError(ctx, w, err)
	return
}

2. アップロード フォームを作成する

フォームにはファイル アップロード用のフィールドを含め、フォームの enctypemultipart/form-data と設定する必要があります。ユーザーがフォームを送信すると、Blobstore API が POST を処理し、blob を作成します。この API はさらに、blob の情報レコードを作成してデータストアに保存し、指定したパスにあるアプリケーションに、blob キーに書き換えたリクエストを渡します。

	var rootTemplate = template.Must(template.New("root").Parse(rootTemplateHTML))

	const rootTemplateHTML = `
<html><body>
<form action="{{.}}" method="POST" enctype="multipart/form-data">
Upload File: <input type="file" name="file"><br>
<input type="submit" name="submit" value="Submit">
</form></body></html>
`

3. アップロード ハンドラを実装する

このハンドラでは、blob キーとともにアプリケーションのデータモデルの残りの部分を保存できます。blob キー自体には、データストアの blob 情報エンティティから引き続きアクセスできます。ユーザーがフォームを送信し、ハンドラが呼び出された時点で、blob はすでに保存され、blob 情報がデータストアに追加されていることに注意してください。アプリケーションに blob を保持しない場合は、すぐに blob を削除して blob が孤立しないようにしてください。

ctx := appengine.NewContext(r)
blobs, _, err := blobstore.ParseUpload(r)
if err != nil {
	serveError(ctx, w, err)
	return
}
file := blobs["file"]
if len(file) == 0 {
	log.Errorf(ctx, "no file uploaded")
	http.Redirect(w, r, "/", http.StatusFound)
	return
}
http.Redirect(w, r, "/serve/?blobKey="+string(file[0].BlobKey), http.StatusFound)

Blobstore がユーザーのリクエストを書き換えると、アップロードされたファイルの MIME パーツの本文が空になり、MIME パーツのヘッダーとして blob キーが追加されます。他のすべてのフォーム フィールドとパーツは保持され、アップロード ハンドラに渡されます。コンテンツ タイプを指定していない場合、Blobstore はファイル拡張子から推定を試みます。コンテンツ タイプが判断できない場合、新しく作成される blob にはコンテンツ タイプ application/octet-stream が割り当てられます。

blob を提供する

blob を提供するには、blob のダウンロード ハンドラをパスとしてアプリケーションに含める必要があります。このハンドラから、該当する blob の blob キーを blobstore.Send に渡す必要があります。次の例では、blob キーは、URL 引数 r.FormValue("blobKey") としてダウンロード ハンドラに渡されています。実際のダウンロード ハンドラでは、他のメソッドやユーザーの操作など任意の手段で blob キーを取得できます。

blobstore.Send(w, appengine.BlobKey(r.FormValue("blobKey")))

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 をすぐに提供したりはしないでしょう。


package blobstore_example

import (
	"context"
	"html/template"
	"io"
	"net/http"

	"google.golang.org/appengine"
	"google.golang.org/appengine/blobstore"
	"google.golang.org/appengine/log"
)

func serveError(ctx context.Context, w http.ResponseWriter, err error) {
	w.WriteHeader(http.StatusInternalServerError)
	w.Header().Set("Content-Type", "text/plain")
	io.WriteString(w, "Internal Server Error")
	log.Errorf(ctx, "%v", err)
}

var rootTemplate = template.Must(template.New("root").Parse(rootTemplateHTML))

const rootTemplateHTML = `
<html><body>
<form action="{{.}}" method="POST" enctype="multipart/form-data">
Upload File: <input type="file" name="file"><br>
<input type="submit" name="submit" value="Submit">
</form></body></html>
`

func handleRoot(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)
	uploadURL, err := blobstore.UploadURL(ctx, "/upload", nil)
	if err != nil {
		serveError(ctx, w, err)
		return
	}
	w.Header().Set("Content-Type", "text/html")
	err = rootTemplate.Execute(w, uploadURL)
	if err != nil {
		log.Errorf(ctx, "%v", err)
	}
}

func handleServe(w http.ResponseWriter, r *http.Request) {
	blobstore.Send(w, appengine.BlobKey(r.FormValue("blobKey")))
}

func handleUpload(w http.ResponseWriter, r *http.Request) {
	ctx := appengine.NewContext(r)
	blobs, _, err := blobstore.ParseUpload(r)
	if err != nil {
		serveError(ctx, w, err)
		return
	}
	file := blobs["file"]
	if len(file) == 0 {
		log.Errorf(ctx, "no file uploaded")
		http.Redirect(w, r, "/", http.StatusFound)
		return
	}
	http.Redirect(w, r, "/serve/?blobKey="+string(file[0].BlobKey), http.StatusFound)
}

func init() {
	http.HandleFunc("/", handleRoot)
	http.HandleFunc("/serve/", handleServe)
	http.HandleFunc("/upload", handleUpload)
}

Google Cloud Storage で Blobstore API を使用する

Blobstore API を使用して Blobstore ではなく Cloud Storage に blob を保存できます。Google Cloud Storage のドキュメントの説明に従ってバケットを設定し、UploadURL 関数に渡す UploadURLOptions でバケットとファイル名を指定する必要があります。アップロード ハンドラでは、返された blob の返されたマップを処理し、後で blob を取得するために必要な Google Cloud Storage のファイル名を明示的に保存しておく必要があります。

Blobstore API を使用して Cloud Storage オブジェクトを提供することもできます。Cloud Storage オブジェクトを配信するには、blob を提供するで説明しているように、BlobKeyForFile を使用して必要な blobkey を生成します。

割り当てと上限

Blobstore 値に使用する領域は、保存データ(課金対象)の割り当て量の計算対象になります。データストアの blob 情報エンティティはデータストア関連の制限に対してカウントされます。Google Cloud Storage は従量課金サービスであり、Cloud Storage の料金表に従って課金されることに注意してください。

システム全体の安全上の割り当て量について詳しくは、割り当てをご覧ください。

Blobstore の使用については特に、システム全体の安全上の割り当て量に加えて次の制限が適用されます。

  • アプリケーションからの API 呼び出し 1 回で読み取ることのできる Blobstore データの最大サイズは 32 MB です。
  • 1 つのフォームの POST でアップロードできるファイルの最大数は 500 です。
こうしたサイズ制限を回避する方法については、Send 関数のドキュメントをご覧ください。