JavaScript
FileAPI
CSV
npm
browserify

ブラウザ上でCSVファイルをパースする

More than 1 year has passed since last update.

背景

CSVファイルのアップロード機能を考えます。
一番シンプルな方法は、formタグにinput type="file"タグを入れて、指定したファイルをPOSTします。

例えば、HTMLタグ/フォームタグ/ファイルの送信欄を作る - TAG index Webサイト です。

問題

CSVファイルのバリデーションにサーバーへの送信が必須です。
POST前にチェックできれば、ユーザが意図しないCSVファイルを指定した場合に、迅速なフィードバックを返せます。
便利なユーザーインタフェースが作れます。

また、CSVファイルの仕様は

  • エスケープ
  • 区切り文字
  • 改行文字
  • ファイルのエンコーディング

を考慮する必要があります。面倒です。
自分では、パーサーを書きたくありません。

解決策

npmパッケージとして公開されているCSVパーサーを利用します。
今回は、csv-parserを使って、ブラウザ上でCSVファイルをパースします。

csv-parserは

id,first_name,last_name,email,gender,ip_address
1,Paul,Hill,phill0@yahoo.com,Male,213.190.9.236

のようなCSVを

{
  "id": "1",
  "first_name": "Paul",
  "last_name": "Hill",
  "email": "phill0@yahoo.com",
  "gender": "Male",
  "ip_address": "213.190.9.236"
}

JavaScriptオブジェクトに変換します。

browserifyを使うと、npmパッケージをブラウザで動くJavaScriptに変換できます。

実装

ユーザが指定したファイルを取得

input type="file"は指定ファイルが変わるとchangeイベントを早出します。
inputfiles属性からFileオブジェクトが取得できます。

document.querySelector('input')
  .addEventListener('change', (e) => {
    const file = e.target.files[0] // ファイルが取れます。
  })

イベントオブジェクトのtarget属性には、input要素(イベントが発生した要素)が入っています。

ファイルの中身を読み込む

File APIのreadAsText関数を使って読み込みます。

readAsText関数は引数にFileオブジェクトを指定できます。

const reader = new FileReader
reader.readAsText(file)

このとき第二引数でエンコードを指定できます。
WindowsのExeclで作ったCSVファイルを読み込むときは'shift-jis'を指定すると、文字化けを回避できます。

ファイルの読み込みは非同期で行われます。読み込みが終わるとonloadハンドラーが呼ばれます。
読み込んだ結果はinputresultプロパティから取得できます。

const reader = new FileReader
reader.onload = (e) => {
  const content = e.target.result // ファイルの中身が取れます。
}

CSVのパース

csv-parserを使います。
csv-parserはStream APIを実装しています。

データ入力

入力データをwrite関数で渡します。

文字列かBufferオブジェクトが指定できます。
readAsTextの読み取り結果は文字列です。そのまま指定できます。

const csv = require('csv-parser')
const stream = csv()

stream.write(e.target.result)

readAsArrayBufferを利用してArrayBufferオブジェクトを指定した場合はBuffuerに変換する必要があります。

const csv = require('csv-parser')
const stream = csv()

stream.write(new Buffer(e.target.result))

データ取得

変換結果はdataイベントハンドラーで取得します。

const csv = require('csv-parser')
const stream = csv()

stream.on('data',(data) => {
  const object = data // objectにパースされた値が取得できます。
})

ソースコード

index.js

index.js
const csv = require('csv-parser')
const stream = csv()

stream.on('data', function(data) {
  console.log(data)
})

document.querySelector('input')
  .addEventListener('change', (e) => {
    const reader = new FileReader
    reader.onload = (e) => {
      stream.write(e.target.result)
    }

    if (e.target.files[0]) {
      const file = e.target.files[0]
      reader.readAsText(file)
    }
  })

index.html

index.html
<input type="file">
<script src="bundle.js" charset="utf-8"></script>

実行

npm init -y
npm i -D browserify csv-parser
node_modules/browserify/bin/cmd.js index.js bundle.js

index.htmlを開きます。

動作イメージ

スクリーンショット 2016-06-24 14.05.04.png

その他

テスト用のCSVファイルはMockaroo - Random Data Generator | CSV / JSON / SQL / Excelで作りました。

参考