LWCでCSVを読み込みApexで一括登録する実装
CSVを画面で選ぶとブラウザ側で行ごとにJSONへ変換し、Apexへ1回だけ渡してDMLも1回で済ませます。文字コードと件数の落とし穴を説明します。
CSVインポートの流れ
CSVを1行ずつApexへ送って1件ずつ登録すると、ループの中でDMLを発行する形になります。件数が増えた日に、DML発行回数の上限に当たります。
この実装は、CSVの中身をブラウザ側でJSONの配列に変換してから、Apexへは1回だけ渡します。Apex側もリストへまとめてからinsertを1回だけ実行します。読み込みはブラウザ、登録はApexという役割分担です。
用意するCSVの形
姓・名・電話番号の3列を持つCSVを想定します。1行目はヘッダーとして扱い、2行目以降をデータとして読み込みます。
| 姓 | 名 | 電話番号 |
|---|---|---|
| Yamada | Taro | 123-4567-8901 |
| Suzuki | Jiro | 234-5678-9012 |
画面にファイル選択とアップロードボタンを置く
lightning-inputのtype="file"でCSVを選ばせます。acceptをMIMEタイプのtext/csvにして絞り込みます。
<template>
<lightning-card
title="LwcCSVファイルアップロード"
icon-name="custom:custom15"
>
<div style="padding: 0 20px;">
<div style="
padding: 10px 0 0 0;
">
<lightning-input
type="file"
name="csv"
label="CSVファイルアップロード"
onchange={onChangeLwcCSVUpload}
accept="text/csv"
></lightning-input>
</div>
<p style="padding: 10px 0 0 0;">{LwcCSVUploadFileName}</p>
<div style="
padding: 20px 0 10px 0;
margin: 0 0 0 -5px;
">
<lightning-button
variant="brand"
label="CSVファイルアップロード"
title="Primary action"
onclick={onClickLwcCSVUpload}
class="slds-m-left_x-small">
</lightning-button>
</div>
</div>
</lightning-card>
</template>CSVを1件選び、選択後にファイル名を表示してからアップロードボタンで送信します。
選んだCSVを行ごとにJSONへ変換してApexへ渡す
FileReaderでCSVをテキストとして読み込み、改行で行に分け、カンマで列に分けてJSONの配列に組み立てます。
import { LightningElement, api } from 'lwc'; import {ShowToastEvent} from 'lightning/platformShowToastEvent'; import uploadCSVFile from "@salesforce/apex/LwcCSVUploadController.uploadCSVFile"; export default class LwcCSVUpload extends LightningElement { @api file = null; @api LwcCSVUploadFileName; data; onChangeLwcCSVUpload(event) { this.file = event.detail.files; this.LwcCSVUploadFileName = this.file[0].name; const fileReader = new FileReader(); fileReader.onloadend = () => { this.data = fileReader.result; } fileReader.readAsText(this.file[0]); } onClickLwcCSVUpload() { if (!this.file) { return } Promise.resolve().then(() => { return new Promise((resolve, reject) => { const rows = this.data.split(/\r\n|\n/); const outputJson = []; for(let i=1 ; i<rows.length ; i++) { const cols = rows[i].split(','); outputJson.push({ LastName: cols[0], FirstName: cols[1], Phone: cols[2], }); } uploadCSVFile({ jsonData: JSON.stringify(outputJson) }) .then(result => { this.data = result; resolve(this.data); this.dispatchEvent( new ShowToastEvent({ title: 'ファイルアップロード成功', message: 'ファイルアップロード成功しました。', variant: 'success', }), ); }) .catch(error => { this.dispatchEvent( new ShowToastEvent({ title: 'ファイルアップロード失敗', message: error, variant: 'error', }), ); }) }); }) .catch(error => { console.log(error) }) .finally(() => { this.template.querySelectorAll('lightning-input').forEach(each => { each.value = ""; }); this.file = null; }); } }
CSVを1行ずつJSONへ変換し、配列にまとめてから一括でApexへ渡します。Apexの呼び出しは1回だけです。
rows[i].split(',')は、値の中にカンマが含まれていない前提の簡易な分割です。住所や備考のように値そのものにカンマが入りうる列を扱うなら、CSVパーサーを別途用意してください。
メタデータでAPIバージョンを指定する
当時の記事では51.0でしたが、2026年9月時点の最新であるSummer '26 = 67.0へ上げます。
<?xml version="1.0" encoding="UTF-8"?> <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata"> <apiVersion>67.0</apiVersion> <isExposed>true</isExposed> <targets> <target>lightning__HomePage</target> </targets> </LightningComponentBundle>
コンポーネントをホームページに配置できるように公開設定を行います。apiVersionは51.0から67.0へ上げています。
Apexで一括登録する
受け取ったJSONをList<Contact>に組み立ててから、insertを1回だけ実行します。ループの中にDMLはありません。
public with sharing class LwcCSVUploadController { @AuraEnabled public static void uploadCSVFile(String jsonData) { List<Contact> uploadConList = new List<Contact>(); List<Object> jsonDataList = (List<Object>)JSON.deserializeUntyped(jsonData); for(Object obj : jsonDataList) { Map<String, Object> mapStrObj = (Map<String, Object>)obj; Contact con = new Contact(); con.LastName = (String)mapStrObj.get('LastName'); con.FirstName = (String)mapStrObj.get('FirstName'); con.Phone= (String)mapStrObj.get('Phone'); uploadConList.add(con); } SavePoint sp = Database.setSavepoint(); try { insert uploadConList; } catch(DmlException e) { Database.rollback(sp); throw e; } } }
JSONをContactのリストへ組み立て、1回のinsertでまとめて登録します。失敗時はSavepointへロールバックしてから例外を投げ直します。
ホームページに配置して有効化する
ホームページを編集モードで開く
「ホーム」タブを開き、右上の歯車アイコンから「ページを編集」を選びます。
コンポーネントを配置する
作成したLWCをキャンバスへドラッグし、「保存」を押します。
有効化する
「有効化」から、このページを表示するユーザーやアプリを選びます。
動作確認
「ホーム」タブから「CSVファイルアップロード」欄でCSVを選びます。ファイル名が表示されたら「CSVファイルアップロード」ボタンを押します。「ファイルアップロード成功」のトーストが表示されたら、取引先責任者の一覧を開き、CSVの行数と同じだけレコードが増えていることを確認します。
ここで間違えやすい
| 間違い | 何が起きるか |
|---|---|
| ExcelでCSVを保存してそのまま使う | Excelの既定の保存形式はUTF-8ではないため、readAsTextで読み込むと日本語が文字化けします |
| 値の中にカンマが含まれる | split(',')は単純な分割なので、列がずれて登録されます |
| 1回のCSVに数万件を詰め込む | ブラウザ側でJSON文字列を組み立ててから1回のApex呼び出しに渡すため、件数が多いとヒープサイズや呼び出しのペイロード上限に当たります。大量件数はデータローダやBulk APIを使う方が安全です |
| ヘッダー行も登録してしまう | iを0から回すと、1行目の見出しまでレコードとして登録されます |
確認した環境
- 2026年9月 / Salesforce Summer '26(APIバージョン67.0)時点の公式ドキュメントで、Bulk APIを使うべき場面とCSVの文字コードの扱いを確認しています
まとめ
- CSVはブラウザ側でJSONへ変換し、Apexへは1回だけ渡します。Apex側の
insertもループの外で1回だけです - ExcelのCSVはUTF-8で保存されないことが多く、そのままだと文字化けします
split(',')は値の中のカンマに対応していません。複雑なCSVには専用のパーサーが必要ですjs-meta.xmlのapiVersionは、当時の51.0からSummer '26の67.0へ上げています- 件数が多いCSVは、この実装のようにブラウザで組み立てて1回で送る方式ではなく、データローダやBulk APIを検討します
Salesforceの導入・運用についてご相談ください
導入前の検討から、お使いの環境の改修・運用、AIとの連携まで承ります。状況を伺ったうえで、進め方をご提案します。