JSPの出力結果をZIPにまとめて出力

仕事で利用している独自フレームワークの都合上
JSPの出力結果をZIPで固めてダウンロード」
する必要がでてきたので、サンプルを作ってみました。

まず、サーブレットの処理はこんな感じ。


public class TestServlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doPost(req, resp);
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {

resp.setContentType("application/zip;charset=Shift_JIS");
resp.setHeader( "Content-Disposition"," attachment;filename=\"data.zip\"");

ZipOutputStream zos = new ZipOutputStream(resp.getOutputStream());
RequestDispatcher dispatcher = req.getRequestDispatcher("/WEB-INF/jsp/testres.jsp");

// 何回か処理実行(結果のレスポンスはJSPの出力結果)
for (int i = 0; i < 5; i++) {
TestResponseWrapper wrapper = new TestResponseWrapper(resp);
dispatcher.include(req, wrapper);
ZipEntry entry = new ZipEntry("test" + i + ".txt");
zos.putNextEntry(entry);
zos.write(wrapper.getWriteDataByJsp().getBytes());
zos.closeEntry();
wrapper.clearStream();
}
zos.close();
}
}



次に、レスポンスを横取りするResponseWrapperはこんな感じ。
※手抜きです

public class TestResponseWrapper extends HttpServletResponseWrapper {

private TestServletOutputStream os;
private CharArrayWriter writer;

public TestResponseWrapper(HttpServletResponse resp) {
super(resp);
os = new TestServletOutputStream();
writer = new CharArrayWriter();
}

@Override
public ServletOutputStream getOutputStream() throws IOException {
return os;
}

@Override
public PrintWriter getWriter() throws IOException {
return new PrintWriter(writer);
}

public String getWriteDataByJsp() {
return writer.toString();
}

public void clearStream() {
os = new TestServletOutputStream();
writer = new CharArrayWriter();
}

/** ※こいつはオマケに近い */
class TestServletOutputStream extends ServletOutputStream {

private ByteArrayOutputStream baos;

public TestServletOutputStream() {
baos = new ByteArrayOutputStream();
}

@Override
public void write(int b) throws IOException {
baos.write(b);
}

public byte[] getWriteData() {
return baos.toByteArray();
}
}
}

今回詰まったのは以下の点。
Tomcatの場合、JSPの出力は#getWriter()経由で出力される。
 ※最初ずっと#getOutputStream()が呼ばれる前提で色々やっていたので見事にハマッた。
 ※コンテナの実装依存部分ですかね?JspWriterで出力してるから依存ではないような気もするけど…中まで追ってないからよくわかりません。
フレームワークが、RequestDispatcher#forward固定になっており、それをincludeと切り替えれるようにするのに
 ものすごく時間がかかった。(※HttpServletを何回も継承[縦の階層が深い]するのはやめてほしいなぁ…)


本当はServletじゃなくて、Filterで実装しようとしてましたけど、邪魔くさくなったので途中でやめ。
上のコードで期待する動作にはなっているので、今回はよしとします。。