この内容は古いバージョンです。最新バージョンを表示するには、戻るボタンを押してください。
バージョン:9
ページ更新者:guest
更新日時:2026-06-11 07:12:00

タイトル: CSVファイルアップロード方法(Ajax)
SEOタイトル: Spring + Ajax CSV アップロード|FormData / MultipartFile 実装

この記事の要点
  • Java/Spring + Ajax で CSV ファイルをアップロードする実装パターン
  • クライアント: FormData に file input を入れて $.ajax で POST
  • processData: false + contentType: false の指定が必須(jQuery が勝手にエンコードしないように)
  • サーバ: コントローラで @RequestParam("file") MultipartFile file で受け取る
  • Spring 側で spring.servlet.multipart.max-file-size を設定(デフォルト 1MB)

概要

フロント側 JavaScript から Java (Spring Framework) で CSV ファイルを受け取る実装例です。Ajax + FormData を使うことで、画面遷移なしでアップロード進捗・完了を扱えます。

HTML

<form id="fileuploadform" enctype="multipart/form-data">
    <input type="file" name="file" id="file" accept=".csv">
    <button type="button" id="upload">アップロード</button>
</form>
<div id="result"></div>

JavaScript (jQuery)

$(&quot;#upload&quot;).click(function() {
    var formData = new FormData($('#fileuploadform').get(0));

    $.ajax({
        method: 'POST',
        url: '/csvUpload',
        data: formData,
        processData: false,    // ★ FormData を文字列化させない
        contentType: false,    // ★ multipart/form-data の boundary を維持
        headers: { 'X-CSRF-TOKEN': csrfToken }  // CSRF 対策(Spring Security)
    }).done(function(res) {
        $('#result').text('成功: ' + res.message);
    }).fail(function(xhr) {
        $('#result').text('失敗: ' + (xhr.responseJSON?.error ?? xhr.status));
    });
});

JavaScript(fetch 版・モダン)

document.getElementById('upload').addEventListener('click', async () => {
    const file = document.getElementById('file').files[0];
    if (!file) { alert('ファイル未選択'); return; }

    const formData = new FormData();
    formData.append('file', file);

    try {
        const res = await fetch('/csvUpload', {
            method: 'POST',
            body: formData,
            headers: { 'X-CSRF-TOKEN': csrfToken }
            // ★ Content-Type は付けない(fetch が自動で multipart/form-data + boundary)
        });
        if (!res.ok) throw new Error('HTTP ' + res.status);
        const data = await res.json();
        document.getElementById('result').textContent = '成功: ' + data.message;
    } catch (e) {
        document.getElementById('result').textContent = '失敗: ' + e.message;
    }
});

Spring Controller

@RestController
public class CsvUploadController {

    @PostMapping("/csvUpload")
    public ResponseEntity<Map<String, Object>> upload(
            @RequestParam("file") MultipartFile file) {

        if (file.isEmpty()) {
            return ResponseEntity.badRequest()
                .body(Map.of("error", "ファイルが空です"));
        }

        // 拡張子チェック
        String name = file.getOriginalFilename();
        if (name == null || !name.toLowerCase().endsWith(".csv")) {
            return ResponseEntity.badRequest()
                .body(Map.of("error", "CSV ファイルのみ対応"));
        }

        int rows = 0;
        try (BufferedReader br = new BufferedReader(
                new InputStreamReader(file.getInputStream(), StandardCharsets.UTF_8))) {

            // BOM スキップ
            br.mark(1);
            int c = br.read();
            if (c != 0xFEFF) br.reset();

            String line;
            br.readLine(); // ヘッダ行スキップ
            while ((line = br.readLine()) != null) {
                String[] cols = line.split(",");
                // DB 登録など処理
                rows++;
            }
        } catch (IOException e) {
            return ResponseEntity.status(500)
                .body(Map.of("error", "読み込み失敗: " + e.getMessage()));
        }

        return ResponseEntity.ok(Map.of("message", rows + " 件を登録"));
    }
}

Spring 側のサイズ上限設定

デフォルトでは Spring Boot のアップロード上限は 1MB / 1MB(ファイル / リクエスト全体)です。大きなファイルを扱う場合は application.properties で拡張:

spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
# 一時ディレクトリ(メモリ閾値超え時の書き出し先)
spring.servlet.multipart.location=/tmp
# メモリに保持する閾値(これを超えるとディスクへ)
spring.servlet.multipart.file-size-threshold=2KB

processData / contentType を false にする理由

オプションデフォルトFormData 送信時に必要な値
processDatatrue(クエリ文字列化)false(FormData をそのまま送る)
contentTypeapplication/x-www-form-urlencodedfalse(ブラウザに任せて boundary 付き multipart に)

fetch API ではこの指定は不要 — body に FormData を入れれば自動的に正しいヘッダになります(むしろ手動で Content-Type を付けると boundary が消えて壊れます)。

よくあるエラー

  • 413 Payload Too Large: ファイルサイズが上限超え → spring.servlet.multipart.max-file-size を増やす。または Nginx の client_max_body_size
  • 403 Forbidden: CSRF トークン未送信(Spring Security)→ X-CSRF-TOKEN ヘッダ追加
  • 415 Unsupported Media Type: contentType: false を忘れている → jQuery が間違った Content-Type を送っている
  • 500 + FileNotFoundException tmp/...: Multipart の一時ディレクトリが書き込み不可 → spring.servlet.multipart.location を見直し
  • 日本語ファイル名が文字化け: file.getOriginalFilename() がブラウザ依存。URL エンコードする運用が安全

セキュリティ注意

  • 拡張子だけでなくマジックバイトもチェック(Polyglot ファイル対策)
  • アップロードしたファイルは Web ルート外に保存(直接 URL でアクセスされないように)
  • ファイル名は サーバ側で UUID 等にリネーム(パストラバーサル防止)
  • 1 リクエストあたりの行数上限を設ける(メモリ枯渇 / DoS 対策)