ReoGrid ReoGrid Web

ExcelからアプリへーxlsxからJSONで構造化データを取り出す

· unvell team
ExcelからアプリへーxlsxからJSONで構造化データを取り出す

多くのアプリは、ユーザーがExcelファイルをアップロードできます — 価格表、連絡先のインポート、勤怠表など — そしてそれをバックエンドが理解できる行に変換する必要があります。難しいのはファイルを読むことではなく、雑然とした実世界のスプレッドシートから整った型付きデータを取り出すことです。

これはそのパイプラインを ReoGrid で実践する手順です。ブラウザで xlsx を読み込み、ヘッダーを検出し、列ごとに型変換し、複数シートを扱い、結果を POST します。クリーンにしたデータをあなたが送ると決めるまで、ファイルはクライアントから出ません。

フル忠実度のワークブック(スタイル・数式・結合)が欲しい? それは ReoGrid JSON — 別の仕事のための別の道具です。この記事はデータだけを取り出す話です。


欲しい形

バックエンドは検証して挿入できる行 — たいていオブジェクトの配列 — を欲しがります。

[
  { "sku": "WID-1", "name": "Widget", "price": 9.99, "qty": 40 },
  { "sku": "GAD-2", "name": "Gadget", "price": 24.5, "qty": 12 }
]

スタイルでも数式ソースでもなく、正しいの値です。そこへ至る方法を見ていきます。


ステップ1 — ブラウザでファイルを読み込む

import { createReogrid } from '@reogrid/pro';

const grid = createReogrid('#grid', { licenseKey: 'YOUR-LICENSE-KEY' });

const input = document.querySelector<HTMLInputElement>('#file')!;
input.addEventListener('change', async (e) => {
  const file = (e.target as HTMLInputElement).files?.[0];
  if (file) await grid.loadFromFile(file);
});

loadFromFile はファイルの解析が完了すると解決するので、await すればデータを読み取る準備ができています。


ステップ2 — ヘッダーで行を読む

シートをオブジェクトに変える堅実な方法は、使用範囲を求め、行0をヘッダーとし、各セルの表示値を読むことです。

import type { ReogridInstance } from '@reogrid/pro';

function sheetToRows(grid: ReogridInstance): Record<string, string>[] {
  const ws = grid.worksheet;

  const snapshot = ws.getExportSnapshot();
  let lastRow = 0, lastCol = 0;
  for (const cell of snapshot.cells) {
    lastRow = Math.max(lastRow, cell.row);
    lastCol = Math.max(lastCol, cell.column);
  }

  const headers: string[] = [];
  for (let c = 0; c <= lastCol; c++) {
    headers[c] = (ws.getDisplayText(0, c) || `col${c}`).trim();
  }

  const rows: Record<string, string>[] = [];
  for (let r = 1; r <= lastRow; r++) {
    const row: Record<string, string> = {};
    for (let c = 0; c <= lastCol; c++) row[headers[c]] = ws.getDisplayText(r, c) ?? '';
    rows.push(row);
  }
  return rows;
}

cell.value ではなく getDisplayText を使います。描画後の値(数式の結果、書式付き数値)を返すので、人が — そしてあなたのデータベースが — 期待する値です。getExportSnapshot().cells から読むので、末尾の空行・空列も自動的に除外されます。


ステップ3 — ヘッダーをマップし型変換する

実世界のスプレッドシートには "Unit Price" のようなヘッダーと "$24.50" のような値があります。スキーマにマップし、型を明示的に変換します。

interface Product {
  sku: string;
  name: string;
  price: number;
  qty: number;
}

const num = (s: string) => Number(s.replace(/[^0-9.\-]/g, '')); // $ や , を除去

function toProducts(rows: Record<string, string>[]): Product[] {
  return rows
    .filter((r) => r['SKU'])                 // 空行をスキップ
    .map((r) => ({
      sku: r['SKU'].trim(),
      name: r['Product Name'].trim(),
      price: num(r['Unit Price']),
      qty: num(r['Quantity']),
    }));
}

(型を推測せず)変換を明示的にしておくことが、インポートを予測可能にします。ここで検証もしましょう — 失敗する行は除外するかフラグを立てます。


ステップ4 — 複数シートを扱う

ワークブックには複数シートがよくあります。ワークブックを反復し、アクティブシートを切り替えて、各シートを抽出します。

function workbookToData(grid: ReogridInstance): Record<string, Record<string, string>[]> {
  const out: Record<string, Record<string, string>[]> = {};
  const wb = grid.workbook;
  for (let i = 0; i < wb.sheetCount; i++) {
    wb.setActiveSheet(i);              // `grid.worksheet` がシート i を指す
    out[grid.worksheet.name] = sheetToRows(grid);
  }
  return out;
}
// → { "Products": [...], "Suppliers": [...] }

ステップ5 — 送信する

await grid.loadFromFile(file);
const products = toProducts(sheetToRows(grid));

const res = await fetch('/api/products/import', {
  method: 'POST',
  headers: { 'content-type': 'application/json' },
  body: JSON.stringify(products),
});

if (!res.ok) throw new Error('Import failed');

解析はブラウザで起きたので、エンドポイントには格闘すべき生ファイルではなく整った検証済み JSONが届きます。サーバー側の xlsx パーサも一時ファイルも不要です。


おまけ: インポート前にユーザーがデータを直せる

ReoGrid はファイルを編集可能なグリッドに描画するので、それを見せて、ユーザーが「インポート」を押す前に誤字を直したり空欄を埋めたりさせ、その編集後の版に sheetToRows を実行できます。1つのコンポーネントが、アップロードのプレビュー・編集・抽出をこなします。


関連

ReoGrid Web を試してみる

React/Vue 向けの Canvas ベース Excel 互換スプレッドシートコンポーネント。 Lite は無料 — npm install 一発で始められます。

関連記事

ニュースレター

開発の最新情報をお届けします

新しいリリース・機能追加・お知らせをいち早く受け取るには、
メーリングリストにご登録ください。