Edited at

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

More than 3 years have 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で作りました。


参考