Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
25
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

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

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

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
25
Help us understand the problem. What are the problem?