ResponseUtil#downloadへの要望
sa-struts-tutorial の DownloadActionをみるとダウンロードのサンプルをみることができる。ResponseUtilには便利にも
download(String fileName, byte[] data)
というメソッドがあるので、さくっとダウンロードさせることができるのだ。サンプルではStringをgetBytesしてファイル化してダウンロードさせているが、実際には実ファイルをダウンロードさせることが多いだろう。それも、
@Execute(validator = false) public String download() { File file = new File("/path/to/file"); ResponseUtil.download(file.getName(), FileUtil.getBytes(file)); return null; }
と、すればできる(ファイル名のエンコーディング対応は省略)
FileUtilというのもorg.seasar.framework.util.FileUtilであり、すぐさま使うことができる。
しかし問題がある。でかいファイルだとFileUtil.getBytes(file)でメモリにデータをためこんでしまうため、Out of Memoryしちゃうのだ。そんなに大きなファイルでなくとも同時に利用するユーザが多いとメモリを圧迫することになる。もちろん、
@Execute(validator = false) public String download() { try { File file = new File("/path/to/file"); response.setContentType("application/octet-stream"); response.setHeader("Content-disposition", "attachment; filename=\"" + file.getName() + "\""); response.setContentLength(file.length()); InputStreamUtil.copy(FileInputStreamUtil.create(file), response.getOutputStream()); } catch (IOException e) { throw new IORuntimeException(e); } return null; }
とゴリって書いてしまえば、重いファイルでもメモリをあまり使わずにスルスルっとダウンロードはさせることはできる。上記のコードで使っているInputStreamUtilクラスやFileInputStreamUtilクラスもSeasarが内包しているクラスなのですぐに使える。
でも、どうせだったら ResponseUtil で対応してほしいなぁと思います。
うーんこんな感じでしょうか。Content-Lengthあり版となし版です。
public static void download(String fileName, InputStream in) { HttpServletResponse response = getResponse(); try { response.setContentType("application/octet-stream"); response.setHeader("Content-disposition", "attachment; filename=\"" + fileName + "\""); OutputStream out = response.getOutputStream(); InputStreamUtil.copy(in, out); } catch (IOException e) { throw new IORuntimeException(e); } } public static void download(String fileName, InputStream in, int length) { HttpServletResponse response = getResponse(); response.setContentLength(length); download(fileName, in); }
直接Fileインスタンスを引数にしちゃうのもありかなぁ。そうしたら内部でfile.length()できるしなぁ。
もしSAStrutsのコミッターの方々がご覧になっていましたら、ぜひご検討いただきたいなと思います。
ちなみに重いファイルは100メガのファイルでためしました。どうやって作ったかというと、コマンドプロンプトから
fsutil file createnew 100mb.data 104857600
と打っただけです(WinXP)。便利ですなぁ。
追記
HttpServletResponse#setContentLength(int) ってあったのね。