.log(ぽちろぐ)

福岡.平尾に住んでるSE(スーパーエロい)の日記。

Struts2 ファイルをダウンロードさせる。

     result type に、StreamResultを使います。
     2.0.12で可能な形のサンプルはwebに落ちてました。そいつを2.1.6で動かしてみたら
    [inputName]ってキーで、inputStreamを寄越せや!
    Struts2様が仰りやがりますので、Struts2のソースを読んで、書き換えたらうまくいきました。
     情報共有の意味で上げておきます。もっといい書き方があるよ、って人は指摘ください。

     要件これ。
    1. ファイルをダウンロードさせたい。
    2. DB(とか)からファイルパスを引っ張ってきて、
    3. 日本語名の付いたファイル、
    4. 拡張子はjpgだったりdocだったり。

     アノテーションはこのように書きます。
    Struts2.1.6
    @Result(name = "download", type = "stream"
            , params = {
            "inputName", "inputStream",
            "contentType", "application/octet-stream; charset=UTF-8",
            "contentLength", "${ contentLength }",
            "contentDisposition", "attachment; filename = ${fileName}"
            })
    
     ちなみにStruts2.0.12
    @Result(name = "download", type = StreamResult.class, value = "inputStream"
    		, params = {
    		"contentType", "application/octet-stream; charset=UTF-8",
    		"contentLength","${ contentLength }",
    		"contentDisposition","attachment; filename= ${fileName}"
    		})
    
     paramsの中はMap状になってて、パラメータが足りないとき、Struts2に怒られます。Struts2.1.6時点では4つ。
    1. inputName
    2. contentType
    3. contentLength
    4. contentDisposition
    というわけで、ソースはこんな感じになるでしょう。
    package 適切な
    import 適当に
    @Results({
    	@Result(name = "download", type = "stream"
    	        , params = {
    	        "inputName", "inputStream",
    	        "contentType", "application/octet-stream; charset=UTF-8",
    	        "contentLength", "${ contentLength }",
    	        "contentDisposition", "attachment; filename = ${fileName}"
    	        })
    	//TODO 他の戻りも定義するかい?
    })
    public class downloadAction {
    	public String fileName;
    	public long contentLength;
    	public InputStream inputStream;
    
    	public String execute() throws Exception{
    		//TODO ダウンロードじゃない場合もあるなら、分岐を仕込んで、successとかerrorとかを返すべし。
    		return this.download();
    	}
    	private String download() throws Exception{
    		//TODO 実際は、DBからファイルパスを取ってきて・・・
    		private String img = "C:\\TEMP\\ほげ.png";
    		File downFile = new File(img);
    		try {
    			this.inputStream = new BufferedInputStream(new FileInputStream(img));
    			this.fileName = URLEncoder.encode(downFile.getName(), "UTF-8");
    			this.contentLength = downFile.length();
    		} catch (FileNotFoundException e) {
    			e.printStackTrace();
    		}
    		return "download";
    	}
    }
    
     ダウンロードダイアログが出て、直で開くと、日本語ファイルは、タイトルが文字化けるです。残念。
     かなり当ったけど、無理っぽい?。
    (X URLエンコードWindows-31JとかSJISとか、application/octet-stream; charset=をWindows-31JとかSJISとか)
     そこは割りきりかなと、現時点では思います。保存しなさいってことで話を持って行くのがベターかなあと。


     contentDispositionの値を、attachment → inlineと書き換えることで、.xlsとか.docを、ブラウザ内で開くこともできるかも。

    ちなみにTIFFを開かせようとして、
    @Result(name = "download", type = "download"
            , params = {
            "inputName", "inputStream",
            "contentType", "image/tiff; charset=UTF-8",
            "contentLength", "${ contentLength }",
            "contentDisposition", "inline; filename = ${fileName}"
            })
    
    とやったけど、対応するアプリケーションが、クライアントにあるや無しや?になるんで、サーバーサイドでの対応は無理?。

    ≪関連記事≫
     アノテーションでの定義はかなり苦しい(無理?) →できました!。
     @いう間にWebアプリを作れる「Struts 2」入門 ←このサンプルでハマった人(俺)に捧ぐ。