kagamihogeの日記

kagamihogeの日記です。

Flex から Servlet にファイルアップロード

むかし Flex 勉強に S2BlazeDS でファイルアップロード・ダウンロード を書いた。けど、Flex というか Flash からは基本的に*1ローカルファイル見れないんで、このテでアップロードは使えない。じゃどうするかってハナシですが、タブン今までのようにフツーに HTTP の POST でファイルアップロードしてやればええんだろうなー、と思ったのでやってみた。

サーバの Java

今回は何かしらのフレームワーク使わずに素の Servlet を使ってやってみます。

パッケージ構成はこんな感じ。

BlazeDS のテストに使ったプロジェクトと同居させたんで、色んなディレクトリがあるんだけど……まぁ気にせず。WEB-INF の下にソース置く男の人って……とか思うのもスルーで……

次に、ライブラリに Apache Commons を使うんで、必要なパッケージを WEB-INF/lib に突っ込んで、パスを通します。Maven2 使わない男の人って(ry

Commons FileUploadCommons 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 アプリには変わりない

ってのを今回改めて実感した次第です。

*1:Adobe AIR と Flash Player 10 除外という意味

*2:さすがに丸々引用じゃアレだろと思ったらしい