| この記事の要点 |
|---|
|
クライアント側(JavaScript / fetch)
// モダンな書き方 (fetch + Blob)
async function downloadCsv() {
const response = await fetch("/api/users/export", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ filters: { status: "active" } })
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
// Blob として取得
const blob = await response.blob();
// ダウンロードリンクを作成して自動クリック
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "users.csv"; // ファイル名
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url); // メモリ解放
}
jQuery 版
// jQuery.ajax を使う場合
$.ajax({
url: "/api/users/export",
method: "GET",
xhrFields: { responseType: "blob" }, // ← Blob として受け取る
success: function(blob, status, xhr) {
// Content-Disposition からファイル名取得
let filename = "download.csv";
const cd = xhr.getResponseHeader("Content-Disposition");
const match = cd && cd.match(/filename="?(.+?)"?$/);
if (match) filename = decodeURIComponent(match[1]);
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
a.remove();
URL.revokeObjectURL(url);
},
error: function(xhr) {
alert("ダウンロード失敗: " + xhr.statusText);
}
});
サーバー側(Spring Boot)
@RestController
@RequestMapping("/api/users")
public class UserExportController {
private final UserService userService;
@GetMapping("/export")
public void exportCsv(HttpServletResponse response) throws IOException {
response.setContentType("text/csv; charset=UTF-8");
response.setHeader("Content-Disposition",
"attachment; filename=\"users.csv\"");
// BOM 付き UTF-8 (Excel 文字化け対策)
response.getOutputStream().write(new byte[]{(byte)0xEF, (byte)0xBB, (byte)0xBF});
try (PrintWriter writer = response.getWriter()) {
// ヘッダ行
writer.println("ID,氏名,メール,作成日");
// データ行 (ストリーミングで大量データ対応)
userService.streamAll().forEach(user -> {
writer.println(String.format("%d,%s,%s,%s",
user.getId(),
csvEscape(user.getName()),
csvEscape(user.getEmail()),
user.getCreatedAt()
));
});
}
}
// CSV エスケープ (カンマ・引用符・改行を含む値)
private String csvEscape(String value) {
if (value == null) return "";
if (value.contains(",") || value.contains("\"") || value.contains("\n")) {
return "\"" + value.replace("\"", "\"\"") + "\"";
}
return value;
}
}
サーバー側(PHP)
<?php
// CSV ダウンロード処理
header("Content-Type: text/csv; charset=UTF-8");
header("Content-Disposition: attachment; filename=\"users.csv\"");
// BOM 付き UTF-8 (Excel 対策)
echo "\xEF\xBB\xBF";
$out = fopen("php://output", "w");
// ヘッダ行
fputcsv($out, ["ID", "氏名", "メール", "作成日"]);
// データ行
$stmt = $pdo->query("SELECT id, name, email, created_at FROM users");
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
fputcsv($out, $row);
}
fclose($out);
exit;
サーバー側(Laravel)
public function export(): StreamedResponse
{
$headers = [
'Content-Type' => 'text/csv; charset=UTF-8',
'Content-Disposition' => 'attachment; filename="users.csv"',
];
return response()->stream(function () {
echo "\xEF\xBB\xBF"; // BOM
$out = fopen('php://output', 'w');
fputcsv($out, ['ID', '氏名', 'メール']);
User::chunk(1000, function ($users) use ($out) {
foreach ($users as $user) {
fputcsv($out, [$user->id, $user->name, $user->email]);
}
});
fclose($out);
}, 200, $headers);
}
サーバー側(Node.js / Express)
app.get("/api/users/export", async (req, res) => {
res.setHeader("Content-Type", "text/csv; charset=UTF-8");
res.setHeader("Content-Disposition", 'attachment; filename="users.csv"');
res.write("\uFEFF"); // BOM
res.write("ID,氏名,メール\n");
// ストリーミングで送る
const stream = db.users.find().stream();
stream.on("data", user => {
res.write(`${user.id},${csvEscape(user.name)},${user.email}\n`);
});
stream.on("end", () => res.end());
});
function csvEscape(s) {
if (s == null) return "";
if (/[,"\n]/.test(s)) return '"' + s.replace(/"/g, '""') + '"';
return s;
}
Excel 文字化け対策
Excel は BOM 付き UTF-8(バイトオーダーマーク EF BB BF)でないと、CSV を Shift_JIS と誤認して文字化けします:
# BOM の付与方法
# Java
out.write(new byte[]{(byte)0xEF, (byte)0xBB, (byte)0xBF});
# PHP
echo "\xEF\xBB\xBF";
# Node.js
res.write("\uFEFF");
# Python
csv_writer.writerow(["..."])
# ファイル open 時に encoding="utf-8-sig" を指定
CSV エスケープのルール
- 値に カンマ
,が含まれる → ダブルクォートで囲む - 値に 改行
\nが含まれる → ダブルクォートで囲む - 値に ダブルクォート
"が含まれる → ダブルクォート 2 個に置換、全体をダブルクォートで囲む
// 例
"Alice, Bob" → "Alice, Bob" # カンマ → 囲む
He said "Hi" → "He said ""Hi""" # " を "" にエスケープ
"Line1
Line2" → "Line1\nLine2" # 改行 → 囲む
大容量データのストリーミング
10 万行以上の CSV を全部メモリに溜めるとサーバが OOM します。ストリーミングで逐次送信:
- JPA:
@Query(... )+Stream<Entity>+@Transactional(readOnly = true) - JdbcTemplate:
query(... , RowCallbackHandler) - Laravel:
chunk()やcursor() - Node:
cursor()/readable stream
関連記事
子ページ
子ページはありません
同階層のページ
- インストール(eclipseプラグイン)
- クイックスタート
- プロジェクトの作成
- Spring Bootプロジェクトの作成
- Spring Bootプロジェクトの実行
- Spring BootでHello World!
- アノテーション一覧
- DB接続設定からエンティティおよびリポジトリの作成、値の取得まで(JPA編)
- DB接続設定や値の取得(JdbcTemplate編)
- ビューから値をモデルに格納しコントローラーで受け取る方法
- コントローラーにてモデルに値を格納してビューに渡す方法
- テンプレートエンジン
- ModelとModelAndViewの違い
- AOPの使用方法
- classpath: 内部ファイルの読み込み
- file: 外部ファイルの読み込み
- CSVファイルアップロード方法(Ajax)
- CSVファイルダウンロード方法(Ajax)
- Spring Bootプロジェクトのビルドと本番環境へのデプロイ方法(内部tomcat使用)
- Application.propertiesの環境依存設定の分割方法
- JPAにおけるEntityManagerの取得方法
- JPAにおけるjava.sql.Connectionの取得方法
- エラー一覧
- jarの引数を受け取る方法
- Spring BootでGmailからメール送信
- 複数のDBに接続する設定(Spring Boot & JPA編)
- ポート番号の変更
- Basic認証の実装と特定のURLに限定する方法
- Spring SecurityのBasic認証の無効化
- 独自のエラーページを定義する方法
- プロパティファイルの値やjar実行時の引数を取得する方法
人気ページ
- 1 Eclipseで「サーバーに追加または除去できるリソースがありません。」の原因と対処法
- 2 tomcat の起動 / 停止ログと catalina.log・catalina.out の違い
- 3 JavaScript で base URL を取得する方法|window.location.origin
- 4 YouTube Data API v3 エラー一覧|403・400・404 の原因と対処
- 5 Laravel エラー一覧|500/Blade/DB 接続/ルーティングの代表エラー
- 6 3Dグラフィックスとは|モデリング/レンダリング/主要ソフトウェア (Blender / Maya)
- 7 Spring Frameworkのアノテーション一覧
- 8 【Spring】@Valueアノテーションとは
- 9 CATALINA_HOME の確認方法 (Linux / Mac)
- 10 【Spring】@Autowiredアノテーションとは
最近更新/作成されたページ
- プロジェクトをTomcatプロジェクトとして認識させる方法 2026-10-07 22:32:50
- MySQLの1366 Incorrect string value|Laravelの文字コード・絵文字エラー 2026-10-07 21:54:03
- curlの証明書ホスト名不一致|旧エラー51・現行60の確認と対処 2026-10-07 21:54:03
- LaravelのMassAssignmentException|fillableの原因と安全な対処 2026-10-07 21:54:03
- Eclipse で Tomcat の起動ログがコンソールに出ない時の確認手順 2026-10-07 21:54:02
- MySQLにおける中央値(Median)の導き方(バージョン8未満) 2026-10-07 13:49:45
- getInputForward 2026-10-07 13:41:15
- JSONから配列に変換 2026-10-07 13:41:15
- ビューから値をモデルに格納しコントローラーで受け取る方法 2026-10-07 13:23:41
- Laravelのテーブル作成と定義変更|マイグレーション・up/down・注意点 2026-10-07 13:23:41
- NumPy 配列に要素を追加する方法 (append / concatenate) 2026-10-07 13:23:41
- MariaDB・MySQLで現在日時を取得する方法|NOW・タイムゾーン・保存型 2026-10-07 13:13:36
- 【django】テンプレートで定数を使用する方法 2026-10-07 13:10:15
- Spring BootにおけるApplication.propertiesの環境依存設定の分割方法 2026-10-07 12:09:35
- Not supported for DML operations【Springエラー】 2026-10-07 11:09:38