kagamihogeの日記

kagamihogeの日記です。

Flex で Servlet からファイルダウンロード

方々を調べていると S2BlazeDS を使用していてもファイルのアップロード・ダウンロードはフツーに Servlet 作ってやったほうが良いというか、それしか方法がない気がする。なので FlexServlet からファイルダウンロードするやり方についてのメモ。

そういえば Adobe AIR と Flash Player 10 ではローカルファイル見れるようになったんで別かもしれないねぇ。

ちなみにアップロードは こっち

とりあえず、OutputStream にデータを書き込むという例のアレ的な処理をする Servlet をこさえる。

public class CsvDownloadServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest reqest, HttpServletResponse response)
        throws ServletException, IOException {
        OutputStream out = null;

        response.setContentType("application/octet-stream");
        response.setHeader("Content-Disposition", "filename=\"downdloadcsv.csv\"");
        
        try {
            out = response.getOutputStream();
            out.write('a');
        } finally {
            out.close();
        }
    }    
}

S2BlazeDS 使ってると忘れてしまいそうな Servlet のマッピングを web.xml に記述する。

    <servlet>
        <servlet-name>csvdownload</servlet-name>
        <servlet-class>{パッケージ名省略}CsvDownloadServlet</servlet-class>
    </servlet>
     ....
    <servlet-mapping>
        <servlet-name>csvdownload</servlet-name>
        <url-pattern>/csvdownload</url-pattern>
    </servlet-mapping>

とりあえずこの段階で、ブラウザのアドレスに http://{servername}:{serverport}/{contextroot}/csvdownload で、ファイルダウンロードができれば Servlet 側は準備おーけー。

次は Flex 側。

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
    <mx:Script>
        <![CDATA[
            private var fileReference:FileReference = new FileReference();
            private function download():void {
                var req:URLRequest = new URLRequest("../csvdownload");
                fileReference.download(req, "downdload.csv");
            }
        ]]>
    </mx:Script>

    <mx:Button click="download()"/>
</mx:Application>

こちら側はいくつかポイントがある。

まず、URLRequest のコンストラクタに指定する URL は絶対パス相対パスも使える。自分の環境では swf ファイルを http://{servername}:{serverport}/{contextroot}/flex/main.swf に置いているので、http://{servername}:{serverport}/{contextroot}/csvdownload には ../csvdownload でアクセスできる。

そして FileReference のインスタンスを作って download メソッドを呼べばファイル保存のダイアログを開くことができる。

ここで気をつけなければいけないのは、FileReference のインスタンスは関数のローカル変数にしてはいけない。ローカル変数にすると、ファイル保存のダイアログは出て、OK を押してもファイルを保存することができない、というか保存されない。

この原因は FileReferenceはローカル変数で使えない | _level0 | Kayac Front End Engineer's Blog に書かれてるように、fileReference.download を押したあと関数はすぐ抜けてしまうので、fileReference の参照が消えてしまうことにあるようだ。非同期実行の落とし穴ですね……