2009年12月15日火曜日

AppEngine1.3.0 Blobstore API入門

AppEngine1.3.0で新しいAPIとして50MBまでのファイルを登録することが出来るBlobstore APIが公開されました。今までは10MBのアップロード、Datastoreには1MBずつしか登録出来なかったので、これは非常に期待がもてるAPIです。折角なので公式ドキュメントを参考に一通り試してみました。

Blobstoreとは?

BlobstoreはDatastoreの厳しいサイズ制限を超えて50MBまでのファイルを登録し、利用することができるサービスです。(Billingを有効にしていないと利用することが出来ません。)このサービスは、Datastoreの様に直接アクセスして利用するのではなく、サービスの受け口に対してWebフォームや、HTTP POSTメソッドによって行われます。従って、アップロード/ダウンロードにかかる処理時間はAppEngineの30秒制限の影響受けることなく利用することが可能なようです。但し、これは検証してないので正確には言えませんが、アプリケーションの中で利用するケースには当てはまらないと思います。

早速試してみる

公式ドキュメントのサンプルコードをそっくりそのまま試してみました。まずはアップロード用のフォームをもったJSPから。
<%@ page language="java" contentType="text/html; charset=UTF-8"
 pageEncoding="UTF-8"%>
<%@ page import="com.google.appengine.api.blobstore.*" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<%
BlobstoreService service = BlobstoreServiceFactory.getBlobstoreService();
String uploadUrl = service.createUploadUrl("/upload");
%>
<form action="<%=uploadUrl%>" method="post" enctype="multipart/form-data">
    <input type="file" name="myFile">
    <input type="submit" value="Submit">
</form>
</body>
</html>
ここではformのactionに対して、BlobstoreService#createUploadUrl(String)を使ってアップロード先となるURLを生成して指定しています。データのアップロードはここで生成されたURLに対して行われます。登録時に必要なその他の処理は、引数で指定している"/upload"にマッピングされるサーブレットなどで行います。それでは次に、"/upload"にマッピングされるサーブレットを見てみましょう。
public class Upload extends HttpServlet {

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {
        BlobstoreService service = BlobstoreServiceFactory.getBlobstoreService();
        Map<String, BlobKey> blobs = service.getUploadedBlobs(req);
        BlobKey blobKey = blobs.get("myFile");
        if (blobKey == null) {
            resp.sendRedirect("/");
        }
        else {
            resp.sendRedirect("/serve?blob-key=" + blobKey.getKeyString());
        }
    }
}

このサーブレットでは、リクエストからアップロードされたデータに対するキーとなるBlobKeyを取り出し処理を行います。実はこのサーブレットが呼び出される前に既にデータのアップロードは完了しています。従ってここでの処理は登録された後に行われます。アップロードされたファイルの妥当性のチェックを行い、削除するなどの処理を行う必要があります。 最後に、上記のUpload.javaでは""/serve?blob-key="+ blobKey.getKeyString()"に対してアクセスし、アップロードしたファイルをダウンロードするサーブレットにリダイレクトしているので、こちらも見てみましょう。
public class Serve extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException,
            IOException {
        BlobstoreService service = BlobstoreServiceFactory.getBlobstoreService();
        BlobKey blobKey = new BlobKey(req.getParameter("blob-key"));
        service.serve(blobKey, resp);
    }
}
名前が若干ややこしくて使う際にちょっと疑問に思いましたが、BlobstoreService#serve(BlobKey, HttpServletResponse)メソッドを利用して、レスポンスにBlobデータを"Serve"することが出来るようです。ここは想像なのですが、30秒制限の影響を受けることなく利用するこが出来るように、レスポンスに書き出す処理は直接行うのではなく、アップロード時と同じように間接的に処理されているはずです。

アップロードしたファイル情報の取得

登録されたデータに関する情報は、下記のようなコードで確認することができます。
BlobInfoFactory factory = new BlobInfoFactory();
BlobInfo blobInfo = factory.loadBlobInfo(blobKey);
BlobKey key = blobInfo.getBlobKey();
String contentType = blobInfo.getContentType();
Date creation = blobInfo.getCreation();
String filename = blobInfo.getFilename();
long size = blobInfo.getSize();
BlobInfoFactoryにはこの他にqueryBlobInfos()メソッド、queryBlobInfosAfter(previousBlob)といったメソッドで指定のBlobKey以降のものを取得することが出来るようです。現時点ではBlobInfoFactoryからはその他のプロパティに対するクエリをかけることは出来ない様です。しかし、BlobInfoFactoryの定数にカインド名やプロパティ名が定義されているのでそれらを使って下記のように取得することは可能です。
DatastoreService datastoreService = DatastoreServiceFactory.getDatastoreService();
for (Entity entity : datastoreService.prepare(new Query(BlobInfoFactory.KIND))
    .asIterable()) {
    BlobInfo info = factory.createBlobInfo(entity);
    // 処理
}

アップロードしたファイルに対する関連

また、エンティティに紐付けて利用するケースもあるかと思います。その際BlobKeyがコアタイプとして利用することができるので、該当のエンティティのプロパティに持たせてあげることによって実現出来ます。試しに登録したほんとに簡単なサンプルが以下になります。
Entity entity = new Entity(KeyFactory.createKey("test", 1));
entity.setProperty("blobKey", new BlobKey("対象のblobKey"));
DatastoreService service = DatastoreServiceFactory.getDatastoreService();
service.put(entity);
このエンティティをDatastore Viewerで見てみると以下のように見えます。

view blobなんていうリンクがあるので、こちらをクリックしてみると、以下のようにイメージが表示されるようです。


まとめ

大容量のファイルを扱えるようになったことは非常に喜ばしいことですが、少なくとも現状ではこれらのデータは静的リソースとしての扱いにとどまる程度と思われます。アプリケーションが利用するファイルとして扱うのは現実的ではありません。(URLFetchする以外に方法はなさそうなので)とはいえ、このBlobstore APIによって動画、写真、音楽ファイルなどの利用が可能になるのは大きいでしょう。

Images APIとの連携(追記)

コメント欄にてtmatsuoさんに教えていただきました。ありがとうございます。
公式のImages APIのドキュメントがこっそり更新されていたようです。これによると、以下の様なコードでBlobstore上の画像ファイルを加工する事が出来るようです。但し、Images APIの制限である1MBまでの制約は残るので用途はドキュメントにもあるようにサムネイルの作成といったところでしょうか。
BlobKey blobKey;  // ...

ImagesService imagesService = ImagesServiceFactory.getImagesService();

Image oldImage = ImagesServiceFactory.makeImageFromBlob(blobKey);
Transform resize = ImagesServiceFactory.makeResize(200, 300);

Image newImage = imagesService.applyTransform(resize, oldImage);

byte[] newImageData = newImage.getImageData();

2 件のコメント:

  1. http://code.google.com/appengine/docs/java/images/overview.html#Transforming_Images_from_the_Blobstore

    Images API の記述がこっそり(笑)更新されています!
    これによると Images API で Blobstore のデータを扱えるが、結果は1MB以下である必要があるみたいですね。

    返信削除
  2. tmatsuoさん情報ありがとうございます。大容量画像のサムネイル構築なんかには使えそうですね!
    記事にも反映させて頂きました。

    返信削除