タイトル: CSVファイルのダウンロード
SEOタイトル: Java Spring + Ajax で CSV ダウンロード|BOM 付き UTF-8 と Blob 実装
| この記事の要点 |
|
概要
本稿は Ajax を用いた CSV ファイルのダウンロード方法を記載します。サーバーサイドは Java の Spring Framework を例にします(適宜読み替えてください)。
普通の <a href> や window.location.href = "/csv" でもダウンロードできますが、認証ヘッダや POST パラメータを送りたい・エラー時に画面遷移したくない場合に Ajax + Blob 方式が便利です。
HTML
<button type="button" id="download">ダウンロード</button>
JavaScript(jQuery)
$("#download").click(function() {
$.ajax({
method: 'POST',
url: '/csvDownload',
xhrFields: { responseType: 'blob' } // ★ バイナリで受け取る
}).done(function(data) {
// BOM を付与(Excel で文字化けしない)
const bom = new Uint8Array([0xEF, 0xBB, 0xBF]);
const blob = new Blob([bom, data], { type: 'text/csv' });
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'data.csv';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}).fail(function(xhr) {
alert('ダウンロード失敗: ' + xhr.status);
});
});
JavaScript(fetch 版・モダン)
document.getElementById('download').addEventListener('click', async () => {
try {
const res = await fetch('/csvDownload', {
method: 'POST',
headers: { 'X-CSRF-TOKEN': csrfToken }
});
if (!res.ok) throw new Error('HTTP ' + res.status);
const blob = await res.blob();
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'data.csv';
a.click();
URL.revokeObjectURL(url);
} catch (e) {
alert('ダウンロード失敗: ' + e.message);
}
});
サーバ(Spring Controller)
@PostMapping(value = "/csvDownload", produces = "text/csv")
public ResponseEntity<byte[]> downloadCsv() {
StringBuilder sb = new StringBuilder();
sb.append("id,name,email\r\n"); // ヘッダ行
sb.append("1,山田太郎,yamada@example.com\r\n");
sb.append("2,鈴木花子,suzuki@example.com\r\n");
// UTF-8 BOM を先頭に付与(Excel 文字化け対策)
byte[] bom = { (byte) 0xEF, (byte) 0xBB, (byte) 0xBF };
byte[] body = sb.toString().getBytes(StandardCharsets.UTF_8);
byte[] result = new byte[bom.length + body.length];
System.arraycopy(bom, 0, result, 0, bom.length);
System.arraycopy(body, 0, result, bom.length, body.length);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.parseMediaType("text/csv; charset=UTF-8"));
headers.setContentDispositionFormData("attachment", "data.csv");
return new ResponseEntity<>(result, headers, HttpStatus.OK);
}
レスポンスヘッダの意味
| ヘッダ | 役割 |
|---|---|
Content-Type: text/csv | CSV ファイルだとブラウザに伝える |
Content-Type: ...; charset=UTF-8 | 文字コード明示。BOM と組み合わせて Excel 文字化けを防ぐ |
Content-Disposition: attachment; filename=... | ダウンロードダイアログを出す。inline だとブラウザ内表示 |
Content-Length | サイズ通知(Spring が自動付与) |
BOM の有無による Excel での挙動
- BOM あり(推奨): UTF-8 と認識して日本語が正しく表示される
- BOM なし: Excel が Shift_JIS と誤認して文字化け(特に Windows 版)
- Excel 以外(VS Code、Linux の
cat)では BOM 無しでも UTF-8 として正しく読める
よくあるハマりどころ
- カンマを含むデータ:
"hello, world"のようにダブルクォートで囲む - ダブルクォートを含むデータ:
"を""にエスケープして全体をダブルクォートで囲む - 改行コード: CSV の慣例は
\r\n。\nでも大体読めるが Excel 安全策で CRLF - 大量データ:
StringBuilderではなくStreamingResponseBodyでストリーミングする - ファイル名の日本語:
filename*=UTF-8''+ URL エンコード形式を使う(RFC 5987)