開発情報・ナレッジ

投稿者: ShiningStar株式会社 2025年8月21日 (木)

CSVファイルをページ上から一括アップロードするサンプルプログラム

ブラウザ上でCSVファイルを解析し、SPIRALのデータベースに一括登録するサンプルプログラムを紹介します。

注意点

このサンプルは、SPIRALのページで動作することを前提としています。
登録先のDBと、CSVの列構成を事前に一致させておく必要があります。
CSVのヘッダー行にはDBの識別名を入れる必要があります。

実装の概要

1. ユーザーがファイル選択フォームでCSVファイルを選ぶ。
2. JavaScriptがファイルを読み込み、ヘッダー行とデータ行に分解してJSON文字列に変換する。
3. 変換したJSONを、フォーム内のhiddenフィールドにセットする。
4. ユーザーが「アップロード」ボタンを押すと、フォームが同じページにPOST送信される。
5. サーバーサイドのPHPがPOSTされたJSONデータを受け取る。
6.
$commonBase->apiCurlAction("POST", "/apps/" . APP_ID . "/dbs/" . DB_ID . "/records/bulk", $payload);

を使用して、データをDBに一括登録する。
7. 処理結果のメッセージを画面に表示する。

HTMLコード (bodyタブ)

まず、ユーザーが操作する画面を作成します。

<!-- 登録結果メッセージの表示 -->
<p th:if="${cp.result.value['result'] != null}" th:text="${cp.result.value['result']}"></p>

<form id="csvForm" method="POST">
  <input type="file" id="csvFile" accept=".csv" required>
  <input type="hidden" name="columns_json" id="columnsJson">
  <input type="hidden" name="data_json" id="dataJson">
  <button type="submit">CSVをアップロード</button>
</form>
            

JavaScriptコード (JavaScriptタブ)

次に、クライアントサイドの動作を担当するJavaScriptです。
CSVファイルを解析し、フォーム送信用のデータを作成します。

document.addEventListener('DOMContentLoaded', function () {
    function parseCSV(text) {
      const rows = [];
      let row = [];
      let value = '';
      let inQuotes = false;
      let i = 0;
  
      while (i < text.length) {
        const char = text[i];
        const next = text[i + 1];
  
        if (inQuotes) {
          if (char === '"' && next === '"') {
            value += '"';
            i++;
          } else if (char === '"') {
            inQuotes = false;
          } else {
            value += char;
          }
        } else {
          if (char === '"') {
            inQuotes = true;
          } else if (char === ',') {
            row.push(value);
            value = '';
          } else if (char === '\n' || char === '\r') {
            if (char === '\r' && next === '\n') i++;
            row.push(value);
            value = '';
            rows.push(row);
            row = [];
          } else {
            value += char;
          }
        }
        i++;
      }
  
      if (value !== '' || row.length > 0) {
        row.push(value);
        rows.push(row);
      }
  
      return rows;
    }
  
    document.getElementById('csvFile').addEventListener('change', function () {
      const file = this.files[0];
      if (!file) return;
  
      const reader = new FileReader();
      reader.onload = function (e) {
        const csvText = e.target.result;
        const parsed = parseCSV(csvText);
        if (parsed.length < 2) {
          alert('データ行が不足しています');
          return;
        }
  
        const columns = parsed[0];
        const data = parsed.slice(1).filter(row => row.length > 0 && row.some(v => v !== ''));
  
        document.getElementById('columnsJson').value = JSON.stringify(columns);
        document.getElementById('dataJson').value = JSON.stringify(data);
      };
      reader.readAsText(file, 'UTF-8');
    });
  });
            

PHPコード (PHPタブ)

最後に、サーバーサイドでAPI連携を行うPHPスクリプトです。
フォームからPOSTされたJSONデータを受け取り、SPIRAL APIを呼び出してDB登録処理を実行します。

<?php
//------------------------------
// 設定値
//------------------------------
define("API_URL", "https://api.spiral-platform.com/v1");
define("API_KEY", "");
define("APP_ROLE", "");
define("APP_ID", "");
define("DB_ID", "");

$columns = $SPIRAL->getParams("columns_json");
$data = $SPIRAL->getParams("data_json");

// POST受信処理
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($columns, $data) && $columns && $data) {

    if (isset($columns[0]) && is_string($columns[0]) && str_starts_with($columns[0], '[')) {
    $columns = json_decode($columns[0], true);
    }
    if (isset($data[0]) && is_string($data[0]) && str_starts_with($data[0], '[')) {
    $data = json_decode($data[0], true);
    }

    if (!is_array($columns) || !is_array($data)) {
        $message = "データ形式が不正です";
    } else {
        $commonBase = CommonBase::getInstance();
        $records = [];

        foreach ($data as $row) {
            $record = [];
            foreach ($columns as $index => $col) {
                if (isset($row[$index])) {
                    $record[$col] = $row[$index];
                }
            }
            $records[] = $record;
        }

        $payload = ["records" => $records];
        $result = $commonBase->apiCurlAction("POST", "/apps/" . APP_ID . "/dbs/" . DB_ID . "/records/bulk", $payload);

        if (isset($result["ids"]) && is_array($result["ids"])) {
        $message = "アップロードと登録に成功しました(ID: " . implode(", ", $result["ids"]) . ")";
        } else {
            $message = "登録エラー:" . json_encode($result, JSON_UNESCAPED_UNICODE);
        }

    }

    $SPIRAL->setTHValue("result", $message);
} else {
    $SPIRAL->setTHValue("result", "");
}

//------------------------------
// 共通モジュール
//------------------------------
class CommonBase {
    /**
     * シングルトンインスタンス
     * @var UserManager
     */
    protected static $singleton;

    public function __construct() {
        if (self::$singleton) {
            throw new Exception('must be singleton');
        }
        self::$singleton = $this;
    }
    /**
     * シングルトンインスタンスを返す
     * @return UserManager
     */
    public static function getInstance() {
        if (!self::$singleton) {
            return new CommonBase();
        } else {
            return self::$singleton;
        }
    }
    /**
     * V2用 API送信ロジック
     * @return Result
     */
    function apiCurlAction($method, $addUrlPass, $data = null, $multiPart = null, $jsonDecode = null) {
        $header = array(
            "Authorization:Bearer ". API_KEY,
            "X-Spiral-Api-Version: 1.1",
        );
        if($multiPart) {
            $header = array_merge($header, array($multiPart));
        } else {
            $header = array_merge($header, array("Content-Type:application/json"));
        }
        if(APP_ROLE){
			$header = array_merge($header, array("X-Spiral-App-Role: ".APP_ROLE));
		}
        // curl
        $curl = curl_init();
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($curl, CURLOPT_URL, API_URL. $addUrlPass);
        curl_setopt($curl, CURLOPT_HTTPHEADER, $header);
        if ($method == "POST") {
            if ($multiPart) {
                curl_setopt($curl, CURLOPT_POSTFIELDS, $data);
            } else {
                curl_setopt($curl, CURLOPT_POSTFIELDS , json_encode($data));
            }
            curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
        }
        if ($method == "PATCH") {
            curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
            curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
        }
        if ($method == "DELETE") {
            curl_setopt($curl, CURLOPT_POSTFIELDS, json_encode($data));
            curl_setopt($curl, CURLOPT_CUSTOMREQUEST, $method);
        }
        $response = curl_exec($curl);
        if (curl_errno($curl)) echo curl_error($curl);
        curl_close($curl);
        if($jsonDecode){
			return $response;
		}else{
            return json_decode($response, true);
		}
    }
}
?>
            

実行結果

ページにアクセスすると、ファイル選択フォームが表示されます。
CSVファイルを選択して「アップロード」ボタンをクリックすると、
ページがリロードされ、処理結果(成功またはエラーメッセージ)が画面上部に表示されます。

まとめ

本記事では、JavaScriptでCSVを解析し、PHPでSPIRALのDBに登録する一連の流れを解説しました。
実際に活用される際はCSVファイルの中身に応じて適切なパースロジックを実装する必要があります。
解決しない場合はこちら コンテンツに関しての
要望はこちら