LoginSignup
29
25

More than 5 years have passed since last update.

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

Last updated at Posted at 2013-11-17

GAEでユーザからのファイルのUploadなどを受け取る場合、選択肢は2つあります。

Blobstoreは昔からあるServiceで、GAEにくっついて存在するServiceです。
GCSはGoogle Cloud Platform の中でFileを扱うことに長けたServiceです。

将来的にはGCSに集約したいという話もあるので、これからGAEでアプリを作る場合は、GCSを選択するのが無難でしょう。

この記事ではGAE/j (Slim3)からGCSのファイルを扱う方法について書きます。

GCSの設定

Productionで動かす場合、GCSの設定を行う必要があります。
GCSの設定については、こちらの記事を参照してください。
Localで試すだけなら、この設定はする必要はありません。

Google Cloud Storage Client Library Download

GAE/jからGCSのファイルを扱うためのLibraryが公式で提供されています。
まずはこれをDownloadします。
いくつか方法が用意されているので、お好きな方法でどうぞ。
https://developers.google.com/appengine/docs/java/googlecloudstorageclient/download

また、Sample もあります。
この記事の内容も、ほとんど公式のSampleどおりです。
Slim3用に少し修正を加えているのと、SampleはファイルをUploadするのではなく、入力した文字列をGCSに書き込んでいるので、それをファイルUploadに変えています。

備考

このSampleでは大きなファイルは扱えません。
大きなファイルを扱いたい場合は、Part2を参照してください。

File Upload

UploadされたFileをGCSに保存しているのが以下のsrcです。
そんなに特筆することもなく、見たまんまという感じです。

public class FileController extends Controller {

    private final GcsService gcsService = GcsServiceFactory
        .createGcsService(new RetryParams.Builder().initialRetryDelayMillis(10)
            .retryMaxAttempts(10).totalRetryPeriodMillis(15000).build());

    ...

    private void doPost() throws IOException {
        FileItem fileItem = requestScope("uploadFile");

        GcsOutputChannel outputChannel =
                gcsService.createOrReplace(getFileName(),
                        new GcsFileOptions.Builder().mimeType(fileItem.getContentType()).build());
        try (OutputStream outputStream = Channels.newOutputStream(outputChannel)) {
            outputStream.write(fileItem.getData());
        }
    }

    private GcsFilename getFileName() {
        // your gcs bucket name and file name
        return new GcsFilename("sample-bucket", "samplefile");
    }
}

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

File Download

GCSのFileをDownloadして返しているのが以下のsrcです。
Uploadと比べて長くなっているのは、Blobstore APIを使う場合と使わない場合があるからです。
Blobstore APIを利用してGCSのFileを操作することもできます。
今のところ、どっち使っても良いような気がしますが、ファイルが大きいとGcsServiceを使うやり方だと、GAEのメモリ上でcopyしているので、60sec制限に引っかかってしまいます。

public class FileController extends Controller {

    private final GcsService gcsService = GcsServiceFactory
        .createGcsService(new RetryParams.Builder().initialRetryDelayMillis(10)
            .retryMaxAttempts(10).totalRetryPeriodMillis(15000).build());

    private static final int BUFFER_SIZE = 2 * 1024 * 1024;

    /** Blobstore APIを使うか */
    public static final boolean SERVE_USING_BLOBSTORE_API = false;


    ...

    private void doGet() throws IOException {
        GcsFilename fileName = getFileName();
        if (SERVE_USING_BLOBSTORE_API) {
            BlobstoreService blobstoreService = BlobstoreServiceFactory.getBlobstoreService();
            BlobKey blobKey =
                    blobstoreService.createGsBlobKey("/gs/" + fileName.getBucketName() + "/"
                            + fileName.getObjectName());
            blobstoreService.serve(blobKey, response);
        } else {
            GcsService gcsService = GcsServiceFactory.createGcsService();
            GcsFileMetadata metadata = gcsService.getMetadata(fileName);
            response.setContentType(metadata.getOptions().getMimeType());
            GcsInputChannel readChannel =
                    gcsService.openPrefetchingReadChannel(fileName, 0, BUFFER_SIZE);

            copy(Channels.newInputStream(readChannel), response.getOutputStream());
        }
    }

    private GcsFilename getFileName() {
        // your gcs bucket name and file name
        return new GcsFilename("sample-bucket", "samplefile");
    }

    private void copy(InputStream input, OutputStream output) throws IOException {
        try {
            byte[] buffer = new byte[BUFFER_SIZE];
            int bytesRead = input.read(buffer);
            while (bytesRead != -1) {
                output.write(buffer, 0, bytesRead);
                bytesRead = input.read(buffer);
            }
        } finally {
            input.close();
            output.close();
        }
    }
}

Github

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

Controller

試したhtml

29
25
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
25