エクセルをダウンロードするサーブレットのサンプル。

エクセルがダウンロードできるサーブレットを作成する機会があり、なかなかにはまったので、そのサンプルを残しておきたいと思います。なおJavaからエクセルを操作するにあたっては[Apache POI](https://poi.apache.org/)を利用していますが、その導入方法や使用方法については省略しています。

まずはServletクラスを以下のように作成します。

```java
package servlet;

import java.io.IOException;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

/**
 * エクセルをダウンロードするサーブレット。
 * @author nekoTheShadow
 *
 */
@WebServlet("/download")
public class ExcelDownloadServlet extends HttpServlet {

	/**
	 * ダウンロードページを表示する。
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.getRequestDispatcher("/WEB-INF/jsp/download.jsp").forward(request, response);
	}

	/**
	 * POSTされたフォームデータに基づきエクセルを生成し、ダウンロードさせる。
	 * @throws UnsupportedEncodingException
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {

		// リクエストの文字コードをUTF-8に変換する。
		request.setCharacterEncoding("UTF-8");

		// リクエストクエリを取得する。
		String param1 = (String) request.getParameter("param1");
		String param2 = (String) request.getParameter("param2");
		String param3 = (String) request.getParameter("param3");

		// ファイル名を生成する: ファイル名はダウンロード日付とする。
		SimpleDateFormat simpleDataFormat = new SimpleDateFormat("yyyy-MM-dd");
		String filename = simpleDataFormat.format(new Date()) + ".xlsx";

		// レスポンスヘッダを設定する。
		response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
		response.setHeader("content-disposition", String.format("attachment; filename=\"%s\"", filename));
		response.setCharacterEncoding("UTF-8");

		// ワークブックとバイトストリーム(レスポンス)を生成する。
		// このふたつはcloseが必ず必要 => try-with-resource文を活用する、
		try (Workbook workbook = new XSSFWorkbook();
			 OutputStream outputStream = response.getOutputStream()) {

			// シートを生成する: 実際は複雑なロジックを用いるが、今回はサンプルなのでシンプルなものにとどめておく。
			Sheet sheet = workbook.createSheet();
			sheet.createRow(1).createCell(1).setCellValue(param1);
			sheet.createRow(2).createCell(1).setCellValue(param2);
			sheet.createRow(3).createCell(1).setCellValue(param3);

			// ワークブックをレスポンスに出力する。
			workbook.write(outputStream);
		} catch (IOException e) {
			e.printStackTrace();
		}

	}


}

```

次に`download`にアクセスした際に表示される`download.jsp`は次の通りです。これは`/WEB-INF/jsp`ディレクトリの直下に配置します。

```jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
</head>
<body>
	<h1>Excel Download</h1>
	<form method="post" action="/excel/download">
		<p><label>パラメタ1:</label><input type="text" name="param1" /></p>
		<p><label>パラメタ2:</label><input type="text" name="param2" /></p>
		<p><label>パラメタ3:</label><input type="text" name="param3" /></p>

		<p><input type="submit" value="Download Excel" /></p>
	</form>
</body>
</html>
```

必要なファイルを準備した後はデプロイし、`/download`にアクセスすると次のようなページが現れるはずです。

![init.PNG](https://qiita-image-store.s3.amazonaws.com/0/105859/94d45afa-81f9-f0dd-af0b-deb120bf4ef2.png)

あとはパラメタを入力して`Download Excel`を押下すると、タイトルが日付となったエクセルファイルがダウンロードされます。

![image](https://qiita-image-store.s3.amazonaws.com/0/105859/4c89671e-d451-bc6b-e44e-4c3651e492b4.png)

そして実際にダウンロードしたエクセルファイルを開いてみると、とりわけ壊れたりなどせず、ちゃんとエクセルファイルが作られていることがわかります。

![excel.PNG](https://qiita-image-store.s3.amazonaws.com/0/105859/2b495226-d31d-a525-d6bd-9ffe48bb5f35.png)

今回特に重要なのはレスポンスヘッダの設定でしょうか。content-typeに指定した`application/vnd.openxmlformats-officedocument.spreadsheetml.sheet`は正直なところあまり見かけないものですし、そもそも`content-disposition`という属性自体が個人的にはなじみが薄く、はまったポイントでした。

```java
// レスポンスヘッダを設定する。
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setHeader("content-disposition", String.format("attachment; filename=\"%s\"", filename));
response.setCharacterEncoding("UTF-8");
```