25
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Last updated at Posted at 2013-11-17

前置き部分は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

Model

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

試したhtml

25
18
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
25
18

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?