angular-file-uploadとGAE/Jでファイルとパラメータを一緒にアップロード
そもそものきっかけ
今作っているウェブサービスで、AngularJSを使っているフロントエンドから、ファイルと一緒に、ユーザの入力値も一緒にアップする必要があったので、その方法をメモ。
angular-file-upload (https://github.com/danialfarid/ng-file-upload)は有名なファイルアップロードのライブラリかと思います。実際のウェブサービスでは、このライブラリを使ってユーザ名と一緒にアイコンとなる画像もアップし、ユーザ情報を登録する・・・ということも多いと思いますが、フロントエンド/バックエンドの両方をカバーした説明がなく結構ハマりました。
フロントエンド
angular-file-uploadの使い方は結構情報があるので特に問題ないかもしれませんが、念のため。
https://github.com/danialfarid/ng-file-uploadからライブラリをダウンロードし、適宜読み込んでください。このあたりは、他のブログやStackOverFlowにお任せします。パラメータは、JSON形式でアップすることになるらしく、下記の記述であれば、(少なくとも僕の環境では)うまく動きました。
Javascript側
$scope.uploadFile = function () { var file = $scope.selectedFile[0]; $scope.upload = $upload.upload({ url: '/upload', method: 'POST', data: {data: parameter}, file: file, }).success(function (data) { //成功時の処理 }); }; $scope.onFileSelect = function ($files) { $scope.selectedFile = $files; };
このonFileSelectを、HTML側から呼び出して、ファイルを変数に格納します。
HTML側
<input type="file" ng-file-select="onFileSelect($files)">
そして、その他の情報(テキスト情報など)を入力させた後、HTML側からuploadFileを呼びます。
で、ここでのキモは、data: {data: parameter}の部分かと思います。このparameterとは何ぞや?というと、
僕の場合は
var parameter = { "id": 1, "title": "title", "tag": "tag", }
のような変数を用意していました。
そしてそれを受け取るバックエンド(Servlet)側の実装です。
バックエンド
@Override public String execute(HttpServletRequest request, HttpServletResponse response) throws Exception { String params = null; Blob thumbnail = null; try { ServletFileUpload upload = new ServletFileUpload(); response.setContentType("text/plain"); FileItemIterator iterator = upload.getItemIterator(request); while (iterator.hasNext()) { FileItemStream item = iterator.next(); InputStream stream = item.openStream(); if (item.isFormField()) { String value = Streams.asString(stream, "iso-8859-1"); value = new String(value.getBytes("iso-8859-1"), "utf-8"); JSONObject object = new JSONObject(value); params = object.getString("data"); } else { + ", name = " + item.getName()); thumbnail = DatastoreUtil .transcodeInputStreamToBlob(stream); } } } catch (Exception e) { }
1つずつ見ていきたいと思います。
FileItemIterator, FileItemStream, ServletFileUpload, Streamsは、org.apache.commons.fileupload.servlet.ServletFileUploadのものです。必要に応じてjarファイルをダウンロード、プロジェクトに追加してください。
if (item.isFormField()) {}は、FORMのパラメータなのか、Blobデータなのかによって場合分けをする必要があるようです。フォームに入力されたテキスト情報はifの方、そうでないものはelseの方ですね。
そして、
String value = Streams.asString(stream, "iso-8859-1"); value = new String(value.getBytes("iso-8859-1"), "utf-8");
は、2バイト文字が文字化けをしないための対策です。もし日本語はじめ、2バイト文字を使う場合は入れておいてください。
あとはJSONObjectにして、そこからフロントエンド側で指定した"data"でオブジェクトを取得します。その後は、煮るなり焼くなりお好きにどうぞ。
また、elseの方では、InputStreamをBlobデータに変換しています(僕の場合はこのあと、Datastoreに格納しています)
このDatastoreUtil#transcodeInputStreamToBlobの中身は、下記のような感じです(まあ、不要かもですが)
public static Blob transcodeInputStreamToBlob(InputStream stream) { if (stream == null) { throw new IllegalArgumentException("Inputstream is null"); } ByteArrayOutputStream bytes = new ByteArrayOutputStream(); try { Streams.copy(stream, bytes, true); return new Blob(bytes.toByteArray()); } catch (IOException e) { } return null; }
これで問題なく動く・・・ハズ!