2.

Java Spring + Ajax で CSV ダウンロード|BOM 付き UTF-8 と Blob 実装

編集
この記事の要点
  • Java/Spring + Ajax で CSV ファイルをダウンロードする実装パターン
  • クライアント: $.ajax でリクエスト、Blob として受け取ってダウンロード
  • サーバ: Content-Type: text/csv + Content-Disposition: attachment; filename=...
  • Excel の文字化け対策にUTF-8 BOM 付与が定石(先頭に 
  • $.ajax はバイナリ未サポートなのでxhrFields: { responseType: "blob" } または素の XHR / fetch を使う

概要

本稿は Ajax を用いた CSV ファイルのダウンロード方法を記載します。サーバーサイドは Java の Spring Framework を例にします(適宜読み替えてください)。

普通の window.location.href = "/csv" でもダウンロードできますが、認証ヘッダや POST パラメータを送りたい・エラー時に画面遷移したくない場合に Ajax + Blob 方式が便利です。

HTML

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 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/csvCSV ファイルだとブラウザに伝える
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)
編集
Post Share
子ページ

子ページはありません

同階層のページ
  1. $.ajax()
  2. CSVファイルのダウンロード