背景
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
イベントを早出します。
input
のfiles
属性から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
ハンドラーが呼ばれます。
読み込んだ結果はinput
のresult
プロパティから取得できます。
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
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
<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を開きます。
動作イメージ
その他
テスト用のCSVファイルはMockaroo - Random Data Generator | CSV / JSON / SQL / Excelで作りました。