Help us understand the problem. What is going on with this article?

GAE/jでファイルを扱う Part2

More than 5 years have passed since last update.

前置き部分はPart1を参照してください。

Part1と違う点

Part1のやり方だと、GAE経由でGCSとFileをやりとりしているので、1requestの60sec制限に縛られ、大きなファイルをやりとりすることができません。
Part2では、そこを改善します。

Part1とPart2で以下のように流れが変わります。
特に変わるのはFileUploadです。

Part1のUploadの流れ

以下の順番でFileが直接渡されている。
1. Client
2. GAE
3. GCS

Part2のUploadの流れ

  1. GAEがFileUpload用のURLを発行する。
  2. ClientはFileUpload用のURLに対して、FileをUploadする。このURLはGAE Frontendを経由しないので、60secにもかからない。
  3. FileUploadが完了すると、1で発行されたURLで動いているServerが、指定されたPathに対して自動で、UploadされたFile情報Parameterに含めてRequestを送ってくる。
  4. 3のRequestをGAEで受けて、UploadされたFileの情報をDatastoreなどに保存する。

File Upload用のURLを発行

Clientに対して、FileをUploadするための専用のURLを発行します。
URLの発行はBlobstore APIを利用して行います。
想像ですが、BlobstoreのためのServer (Picasaと同じServer?)がFileを受け取って、GCSに保存しているんじゃないかなと思います。

Bucket名しか指定できず、UploadされたファイルはBucketの直下に置かれます。
ファイル名はUUIDのような値になります。

"/uploadFile"がUploadが完了した時に、叩かれるPathになります。

public class UploadFileController extends Controller {

    private static final String BUCKET = "sample-bucket";

    private BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();


    private void respondUploadUrl() throws Exception {
        final long MEGA_BYTE = 1024 * 1024 * 1024;

        final UploadOptions options =
                UploadOptions.Builder.withGoogleStorageBucketName(BUCKET).maxUploadSizeBytes(
                        100 * MEGA_BYTE);
        final String url =
                BlobstoreServiceFactory.getBlobstoreService().createUploadUrl("/uploadFile",
                        options);
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/json");
        response.getWriter().println("{\"url\":\"" + new URL(url).getPath() + "\"}");
        response.flushBuffer();
    }
}

発行されるPathは以下の様な感じです。
表からは見えないけど、GAEの中にこのPathを受け取る誰かがいるのでしょう。
/_ah/upload/aglzaW5wa21ubXNyHAsSFV9fQmxvYlVwbG9hZFNlc3Npb25fXxj5Agw

File Upload後に呼ばれる処理

Upload用のURLを発行した時に指定した"/uploadFile"を受け取るのが以下のsrcです。
request parameterにUploadされたFileの情報を入れて送ってくるので、それを受け取って、Datastoreに保存しています。

public class UploadFileController extends Controller {

    private static final String BUCKET = "sample-bucket";

    private BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();


    ...


    private void receiveBlobServiceRedirect() throws Exception {
        final Map<String, List<BlobInfo>> infoListMap = blobstoreService.getBlobInfos(request);

        for (Entry<String, List<BlobInfo>> entry : infoListMap.entrySet()) {
            System.out.println(entry.getKey());

            for (BlobInfo info : entry.getValue()) {
                BlobContent content = new BlobContent(info);
                Datastore.put(content);
            }
        }
    }
}

保存されたFileの情報を管理するために、Datastoreに保存する。
そのためのModel

BlobContent.java
@Model
public class BlobContent {

    /** BlobKey */
    @Attribute(primaryKey = true)
    Key key;

    String filename;

    String contentType;

    Long size;

    Date creation;

    String md5Hash;


    /**
     * {@link Key} 生成
     * @param blobKey 
     * @return {@link Key}
     * @author sinmetal
     */
    public static Key createKey(BlobKey blobKey) {
        return Datastore.createKey(BlobContent.class, blobKey.getKeyString());
    }

    /**
     * the constructor.
     * @category constructor
     */
    public BlobContent() {
    }

    /**
     * the constructor.
     * @param info
     * @category constructor
     */
    public BlobContent(BlobInfo info) {
        this.key = createKey(info.getBlobKey());
        this.filename = info.getFilename();
        this.contentType = info.getContentType();
        this.md5Hash = info.getMd5Hash();
        this.creation = info.getCreation();
        this.size = info.getSize();
    }

    ...

}

UploadされたFileは、Google Cloud Console から見えることができます。
また、GAE ConsoleのBlob Viewerからも見ることできます。
ただ、Blobstoreには実際のファイルは保存されておらず、Blob Viwerで情報を見ることができるだけのようです。
GCS上のファイルを消すと、Blob ViwerからのDownloadが失敗するようになります。
逆にBlob Viwerからファイルを削除すると、GCS上のファイルも消えます。

実際に利用する場合は、Datastoreにも情報を保存しているでしょうから、データの整合性を保つために、削除などはGAEのAppを通して行うのが、真っ当でしょう。

Localで試した場合は、Development Console Datastore ViewerのGsFileInfoというKindで保存されている情報を見ることができます。

File Download

FileのDownloadはBlobstore APIを利用して行います。
UploadしたFileのBlobKey.getKeyString()をparameterとして受け取り、ResponseにそのFileを返しています。

因みにBlobKeyの中身は以下のようになっており、GCSに関係あるということが書いてあります。

encoded_gs_key:c2lucGttbm1zLXByby9xdDBLWFNQZDNJSnVlSUxPRW5tSXhn
public class UploadFileController extends Controller {

    private static final String BUCKET = "sample-bucket";

    private BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();

    @Override
    protected Navigation run() throws Exception {
        switch (request.getMethod()) {
            case "GET":
                String key = request.getParameter("key");
                if (StringUtil.isEmpty(key)) {
                    respondUploadUrl();
                } else {
                    // BlobKey.getKeyString()をparameterとして受け取る。
                    downloadGcsFile(new BlobKey(key));
                }
                break;
            ...
        }
        return null;
    }

    ...

    private void downloadGcsFile(BlobKey blobKey) throws Exception {
        BlobInfo blobInfo = new BlobInfoFactory().loadBlobInfo(blobKey);
        if (StringUtil.isEmpty(blobInfo.getContentType()) == false) {
            response.setContentType(blobInfo.getContentType());
        } else {
            response.setContentType("application/octet-stream");
        }
        response.setContentLength((int) blobInfo.getSize());
        blobstoreService.serve(blobKey, response);
    }
}

Github

srcはGithubに置いてあります。

Controller

https://github.com/sinmetal/gcs-client-library-sample/blob/master/src/main/java/org/sinmetal/gcsexample/controller/UploadFileController.java

Model

https://github.com/sinmetal/gcs-client-library-sample/blob/master/src/main/java/org/sinmetal/gcsexample/model/BlobContent.java

直接関係ないけど、BlobContent一覧を返すController

https://github.com/sinmetal/gcs-client-library-sample/blob/master/src/main/java/org/sinmetal/gcsexample/controller/BlobContentController.java

試したhtml

https://github.com/sinmetal/gcs-client-library-sample/blob/master/src/main/webapp/fileupload-direct.html

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした