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

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

More than 5 years have passed since last update.

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

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

試したhtml

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

sinmetal
GCPUG Admin https://gcpug.jp
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
ユーザーは見つかりませんでした