Node.jsで複数のファイルをコピーしたい。
linuxなら
$ cp src/*.html dest
でできるやつだ。
これをNode.jsでやりたい。
なんかライブラリあるだろ、と思ってnpmレポジトリを2分ほど探したが良いのが見つからず。
いや多分よく探せば絶対あるんだけど探すのめんどくさい。
作るか。
ファイルコピーを実装する
まずは一つのファイルをコピーする関数を作る。
fs
モジュールの.createReadStream()
と.createWriteStream()
を使えば一瞬だ。
var r = fs.createReadStream(src),
w = fs.createWriteStream(dest);
r.pip(w);
だけどこれだけだとエラーハンドリングとか足らない。
もう少し丁寧にかくとあれだ。
function _copySingle(src, dest, callback) {
var aborted = false;
var r = fs.createReadStream(src),
w = fs.createWriteStream(dest);
r.on("error", function (err) {
done(err);
});
w.on("error", function (err) {
done(err);
});
w.on("close", function (ex) {
done();
});
r.pipe(w);
function done(err) {
if (aborted) {
return;
}
callback(err);
aborted = true;
}
}
次にワイルドカードをまとめて処理できるようにする。
こういう時はasync
とglob
モジュールを使うに限る。限らないけど。
var argx = require('argx'),
async = require('async'),
path = require('path'),
glob = require('glob'),
mkdirp = require('mkdirp'),
fs = require('fs');
async.waterfall([
function (callback) {
async.concatSeries([].concat(src), glob, callback);
},
function (src, callback) {
_isExistingDir(dest, function (destIsDir) {
async.each(src, function (src, callback) {
var srcFilename = path.resolve(src),
destFilename = destIsDir ? path.join(dest, path.basename(src)) : path.resolve(dest);
async.series([
function (callback) {
if (options.mkdirp) {
mkdirp(path.dirname(destFilename), callback);
} else {
callback(null);
}
},
function (callback) {
_copySingle(srcFilename, destFilename, callback);
}
], callback);
}, callback);
});
}
], callback);
function _isExistingDir(filename, callback) {
fs.exists(filename, function (exists) {
if (exists) {
fs.stat(filename, function (err, stat) {
callback(!err && stat.isDirectory());
});
} else {
callback(false);
}
});
}
npmパッケージにまとめる
あとはパパッとまとめてテスト書いていつも通りnpmパッケージとして公表。
似たようなのいっぱいありそうだけどとりあえず気にしない。
できた。