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