はじめに
「Node.js - chokidar と forever でファイルとディレクトリの変更を検知してログする - Qiita」で実装したファイル検知の仕組みを応用して、CSVファイルの読み込みと差分検査を行う処理を実装してみました。
作るもの
今回の実装するアプリケーションの概要をまとめます。
前提条件
前提として、以下の npm
と node
がインストール済みであるものとします。
command_powershell
> node -v
v10.16.0
> npm -v
6.10.1
動作仕様
今回のアプリケーションは次のような構成で動作することを想定します。
アプリケーションは次のような機能を持ちます。
- ファイルの変更検知は
chokidar
を使用します。 - 変更検知する対象ディレクトリは 複数の異なるディレクトリ です。
- CSVファイルが更新されたら、既存のバックアップファイルと差分を比較します。
- 比較結果はコンソール出力します。
- 比較が終わったら、既存ファイルを更新されたファイルで上書きます。
実装
実装に関する情報をまとめます。
バージョン
関連モジュールのバージョンは次の通りです。
- node : v10.16.0
- npm : 6.10.1
- chokidar : 3.0.2
- fs : 0.0.1-security
- csv-parse : 4.4.3
- deep-diff : 1.0.2
- forever : 1.0.0
初期設定
まず、リポジトリを作成し、必要なモジュールをインストールします。
command_bash
# プロジェクトフォルダの作成
> mkdir node-app && cd $_
# リポジトリの初期化
> npm init -y
# 関連モジュールのインストール
> npm install --save chokidar
> npm install fs
> npm install csv-parse
> npm install deep-diff
# Global Install.
> sudo npm install -g forever
最終的には次のようなディレクトリ構成になります。
矢印で示した資源は自分で作成します。
command_bash
> tree -L 2
├── backup
│ └── input.csv <-- 既存のバックアップファイル
├── data
│ └── input.csv <-- 更新するファイル(1)
├── data-001
│ └── input.csv <-- 更新するファイル(2)
├── node_modules
├── package-lock.json
├── package.json
└── watch.js <-- アプリケーション本体
コード
完成版のコードは次の通りです。
watch.js
watch.js
'use strict';
// require
var chokidar = require("chokidar");
const fs = require('fs');
const csvSync = require('csv-parse/lib/sync');
var diff = require('deep-diff')
// Constants
// ________________________________
// Monitoring Target Directory
const target_directory_01 = 'data/';
const target_directory_02 = 'data-001/';
const backup_filename = 'backup/input.csv';
// Something to use when events are received.
const log = console.log.bind(console);
// Initialize
// ________________________________
// initialize chokidar
var watcher = chokidar.watch(target_directory_01, {
ignored: /[\/\\]\./,
persistent: true
});
// Add monitoring target.
watcher.add(target_directory_02);
// Monitoring
// ________________________________
// Ready for changes
watcher.on('ready', function () {
// ready
log('Initial scan complete. Ready for changes');
// watched Paths
var watchedPaths = watcher.getWatched();
log("watchedPaths :", watchedPaths);
// Files
// _ _ _ _ _ _ _ _ _ _ _ _ _ _
// Detect File added
watcher.on('add', function (path, stats) {
log(`File ${path} has been added`);
if (stats) log(`File ${path} added size to ${stats.size}`, stats);
var watchedPaths = watcher.getWatched();
log("watchedPaths :", watchedPaths);
});
// Detect File changed
watcher.on('change', function (path, stats) {
log(`File ${path} has been changed`);
if (stats) log(`File ${path} changed size to ${stats.size}`, stats);
// 変更前後の CSVファイルを読み込みオブジェクトに変換する。
let updatedData = readCSV(path);
let originalData = readCSV(backup_filename);
// 変更前後の CSVファイルの差分を検査する。
var differences = diff(originalData, updatedData);
log("differences: ", differences);
log("differences: ", JSON.stringify(differences));
// 変更後のCSVファイルで、変更前のCSVファイルを上書きする。
fs.copyFileSync(path, backup_filename);
});
// Detect File removed
watcher.on('unlink', function (path) {
log(`File ${path} has been removed`);
});
// Directories
// _ _ _ _ _ _ _ _ _ _ _ _ _ _
// Detect Directory added
watcher.on('addDir', function (path) {
log(`Directory ${path} has been added`);
});
// Detect Directory removed
watcher.on('unlinkDir', function (path) {
log(`Directory ${path} has been removed`);
});
// Error
// _ _ _ _ _ _ _ _ _ _ _ _ _ _
// Detect Watcher Error
watcher.on('error', function (path) {
log(`Watcher error: ${error}`);
});
});
/**
* CSVファイルを読み込む。
*
* @param {String} path
* @return {Object} res
*/
function readCSV(path) {
let data = fs.readFileSync(path);
let res = csvSync(data);
return res;
}
データ
事前に用意するテスト用の CSVデータは次の通りです。
data/input.csv
2019/07/15,yamada,000001,100
2019/07/16,tanaka,000100,240
2019/07/17,sasaki,010000,260
2019/07/19,suzuki,200000,400
2019/07/20,okayama,003000,301
backup/input.csv
2019/07/15,yamada,000001,100
2019/07/16,tanaka,000100,240
2019/07/17,sasaki,010000,260
2019/07/19,suzuki,200000,400
2019/07/20,okayama,003000,301
デバッグ
動作確認は次のように行います。
node watch.js
でアプリケーションを起動したら、監視対象のフォルダ配下で適当な操作をしてみましょう。
command_bash
> node watch.js
Initial scan complete. Ready for changes
watchedPaths : {}
# `data/input.csv` に次の行を追加して保存します。
# 2019/07/20,okayama,003000,303
File data/input.csv has been changed
File data/input.csv changed size to 180 Stats {
dev: 12,
mode: 33279,
nlink: 1,
uid: 1000,
gid: 1000,
rdev: 0,
blksize: 512,
ino: 11258999068805782,
size: 180,
blocks: 0,
atimeMs: 1563177207926.31,
mtimeMs: 1563181200379.9946,
ctimeMs: 1563181200379.9946,
birthtimeMs: 1563181200379.9946,
atime: 2019-07-15T07:53:27.926Z,
mtime: 2019-07-15T09:00:00.380Z,
ctime: 2019-07-15T09:00:00.380Z,
birthtime: 2019-07-15T09:00:00.380Z }
differences: [ DiffArray {
kind: 'A',
index: 5,
item: DiffNew { kind: 'N', rhs: [Array] } } ]
differences: [{"kind":"A","index":5,"item":{"kind":"N","rhs":["2019/07/20","okayama","003000","303"]}}]
おわりに
Node.js
の使い勝手の良さに驚いています。
参考
今回の記事で参考にしたサイトを記載します。
csv-parse
use strict
- 【JavasScript】use strictとは - Qiita
- Node.jsにおけるuse strictの挙動 - Qiita
- Node.jsでlog4jsを使ってログを出力する - Developers.IO