むかし Flex 勉強に S2BlazeDS でファイルアップロード・ダウンロード を書いた。けど、Flex というか Flash からは基本的に*1ローカルファイル見れないんで、このテでアップロードは使えない。じゃどうするかってハナシですが、タブン今までのようにフツーに HTTP の POST でファイルアップロードしてやればええんだろうなー、と思ったのでやってみた。
サーバの Java 側
今回は何かしらのフレームワーク使わずに素の Servlet を使ってやってみます。
パッケージ構成はこんな感じ。
BlazeDS のテストに使ったプロジェクトと同居させたんで、色んなディレクトリがあるんだけど……まぁ気にせず。WEB-INF の下にソース置く男の人って……とか思うのもスルーで……
次に、ライブラリに Apache Commons を使うんで、必要なパッケージを WEB-INF/lib に突っ込んで、パスを通します。Maven2 使わない男の人って(ry
Commons FileUpload と Commons IO を使います。今回は FileUpload 1.2.1 と Commons IO 1.4 を使用。Commons IO を WEB-INF/lib に置いとかないと ClassNotFoundException が出ます。FileUpload 1.2.1 は Commons IO に依存しとるので。
とりあえず、ブラウザからファイルアップロードできるものを作ってみる。@IT:Java TIPS -- ファイルアップロード処理を簡単にする(Commons活用) をパクリつつ。
upload.jsp の中身。モロぱくりです。
<%@ page language="java" contentType="text/html; charset=windows-31j" pageEncoding="windows-31j"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=windows-31j"> </meta> <title>ファイルのアップロード</title> </head> <body> <form method="POST" enctype="multipart/form-data" action="ultest"> ファイルパス: <input type="file" name="fl" size="75" /> <input type="submit" value="アップロード" /> </form> </body> </html>
hoge.UploadServlet の中身。@IT のヤツは古いバージョンの FileUpload を使ってるので、プログラマの雑知識 - Commons FileUpload とか 利用手順 - Jakarta Commons FileUploadの使い方 とかからコピペ。doGet はイランのだけど、まぁ一応確認用に。
package hoge; import java.io.File; import java.io.IOException; import java.io.PrintWriter; import java.util.Iterator; import java.util.List; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; public class UploadServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setContentType("text/html; charset=Shift_JIS"); PrintWriter out = resp.getWriter(); req.setCharacterEncoding("Shift-JIS"); out.println("<html>"); out.println("<head>"); out.println("<title>Upload Test!</title>"); out.println("</head>"); out.println("<body>"); out.println("<h1>FileUpload Test</h1>"); out.println("</body>"); out.println("</html>"); } private static final long serialVersionUID = 1L; @Override public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String path = getServletContext().getRealPath("WEB-INF/data"); DiskFileItemFactory factory = new DiskFileItemFactory(); ServletFileUpload sfu = new ServletFileUpload(factory); try { List list = sfu.parseRequest(request); Iterator iterator = list.iterator(); while (iterator.hasNext()) { FileItem item = (FileItem) iterator.next(); /* 取り出したFileItemに対する処理 */ if (!item.isFormField()) { // アップロードファイルの元ファイル名を取得 String strNam = item.getName(); if (strNam != null && !strNam.equals("")) { strNam = (new File(strNam)).getName(); try { item.write(new File(path + "/" + strNam)); } catch (Exception e) { e.printStackTrace(); } } } } } catch (FileUploadException e) { e.printStackTrace(); } PrintWriter out = response.getWriter(); out.print("hoge"); } }
web.xml にお決まりの内容を追記。
<servlet> <servlet-name>uploadtest</servlet-name> <servlet-class>hoge.UploadServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>uploadtest</servlet-name> <url-pattern>/ultest</url-pattern> </servlet-mapping>
とりあえずコレで http://localhost:8080/blazeds1/ultest にアクセスして FileUpload Test と表示されるか確認してみる。
次に、http://localhost:8080/blazeds1/upload.jsp にアクセスしてファイルをアップロードをしてみる。WEB-INF/data にテキトーなファイルが保存されて、ブラウザに hoge と表示されればサーバ側の準備完了。
クライアントの Flex 側
クライアント側も、codeなにがし SAStruts(Super Agile Struts)あれこれ を参照にソースパクリで済ませます。今回丸コピばっかでサーセン。
メインとなる mxml の内容。
<?xml version="1.0" encoding="utf-8"?> <mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:util="util.*"> <mx:ApplicationControlBar width="137" top="15" left="15"> <mx:Button label="ファイルアップロード" click="fileUploader.upload()"/> </mx:ApplicationControlBar> <mx:ProgressBar id="progressBar1" x="10" y="71" mode="manual"/> <util:FileUploader id="fileUploader" fileUploadProgressBar="{progressBar1}" /> </mx:Application>
次にアップロードを行う ActionScript のコード。コピペ元にちょい手を加えて、アップロード中の様子を ProgressBar に表示するようにしてます*2。あと、サーバ側は hoge としかレスポンスを返さないんで uploadDataHandler は失敗側の分岐処理にいきようがないんだけど、まぁ気にしないでください。ホントーは処理結果格納した XML なり JSON なりテキストなり返さないとアレなんだろうけど、まぁ手抜きしてます。
package util { import flash.events.DataEvent; import flash.events.Event; import flash.events.ProgressEvent; import flash.net.FileReference; import flash.net.URLRequest; import flash.net.URLRequestMethod; import flash.net.URLVariables; import mx.controls.Alert; import mx.controls.ProgressBar; public class FileUploader { //ファイルをアップロードしたりダウンロードしたりするクラス private var _fileRef:FileReference; public var fileUploadProgressBar:ProgressBar; public function FileUploader() { } public function upload():void { _fileRef = new FileReference(); _fileRef.addEventListener(Event.SELECT, selectHandler); _fileRef.addEventListener(Event.COMPLETE, completeHandler); _fileRef.addEventListener(DataEvent.UPLOAD_COMPLETE_DATA, uploadDataHandler); _fileRef.addEventListener(ProgressEvent.PROGRESS, fileUploadProgressHandler); //ファイル選択ダイアログを起動しましょう。 _fileRef.browse(); } private function fileUploadProgressHandler(event:ProgressEvent):void { fileUploadProgressBar.setProgress(event.bytesLoaded, event.bytesTotal); } //ファイル選択ダイアログでファイルを選択するとコールされます。 private function selectHandler(event:Event):void { //ファイルデータ以外のリクエストパラメーター var params:URLVariables = new URLVariables(); params.title = "なにかのファイルです〜"; //アクションメソッド"upload"を指定、値は適当です。 params.upload ="action_method"; //リクエストオブジェクトを生成 var request:URLRequest = new URLRequest("http://localhost:8080/blazeds1/ultest"); request.method = URLRequestMethod.POST; request.data = params; //アップロード _fileRef.upload(request, "formFile"); } private function completeHandler(e:Event):void { //このイベントだとサーバーの処理結果データが受け取れないんですよね〜 //なので、DataEvent.UPLOAD_COMPLETE_DATAで処理結果判定します。 } private function uploadDataHandler(event:DataEvent):void { var result:String = new String(event.data); if( result == "hoge" ) { Alert.show("アップロード成功です"); } else { Alert.show("サーバー処理失敗\n\n理由 : " + result); } } } }
実行の様子はこんな感じ。大きめサイズのファイルを選んでやればチリチリとバーが伸びていくのが見れます。
Flex といっても Web アプリには変わりない
ってのを今回改めて実感した次第です。