1.変更されたファイルのみをストレージに保存します。
2.変更されたファイルをパックします。
3.自由になる。
しかし、検索では何も得られませんでした。 むしろ、検索で得られましたが、通常はアイテムの1つが欠落していました。 そのため、私は自分で独自のNodeJSを作成することを決定しました。 2日間書いた。 さらに、暗号化をプログラムにねじ込みました。 暗号化モジュールはノードの標準配信に含まれているため、特別な問題は発生しませんでした。 同時に、パッケージング、暗号化、およびストレージモジュールを拡張可能にしました。 新しい機能を追加するだけで機能を拡張できます。 現在のバージョンでは、保存はファイルシステムでのみ機能し、パッケージ化はされませんが、暗号化されます。
ここで使用方法のヘルプを見ることができます 。
これは終了する可能性があります-ジョブは完了しました。 しかし、マイナスが1つあります。プログラムは同期して動作します。 ギガバイトの情報を保存しない場合は非常に高速に動作しますが、それでもノードは非同期作業に焦点を合わせています。 そこでNode.JSでScreencastを見て、ノードの機能を考慮して、すべてを「正しい」方法で実行することにしました。
「正しく」書くことを決めたので、作業の一般的なスキームから始めます。

この図は、並行して実行できる操作とできない操作を明確に示しています。
ブロックの実装を始めましょう。 各ブロックを個別のファイルに書き込むので、より明確で直感的になります。
1.保存するファイルのリストを定義する
タスクは簡単です-入力時にディレクトリがあり、各要素のサイズ、チェックサム(md5を使用します)、変更日に関するプロパティを取得する必要があります。 変更された要素をさらに判断するためにこれが必要です。 偶然ではなく、ファイルではなく要素を書きます。 ディレクトリのこれらの値を決定します。 ディレクトリサイズは、ネストされたすべてのアイテムのサイズです。 ディレクトリチェックサムは、子のすべてのチェックサムのチェックサムです。 したがって、ディレクトリに変更があったかどうかを理解するには、ストレージとディスク上のこのディレクトリのチェックサムを比較するだけで十分です。
この機能の同期実装
(function(){ // var fs = require('fs'); var path = require('path'); var crypto = require('crypto'); // function _get_attributes(itemName,basePath) { // //console.info(basePath,"\t\t\t",itemName); // var retSize = 0; // var retHash = crypto.createHash('md5'); // . var retInfo; // var filepath = path.join(basePath,itemName); // var stat = fs.statSync(filepath); // if( stat.isDirectory() ) { // retInfo = {}; // var items = fs.readdirSync(filepath); var hashs = []; for(var i in items) { // var attr = _get_attributes(items[i],filepath); // retSize += attr[0]; // hashs.push(attr[1]); // retInfo[ items[i] ] = attr; } // hashs.sort(); for(var j in hashs) { retHash.update(hashs[j]); } } else { // retSize = stat.size; // // var buffer = new Buffer(64*1024); // var fdr = fs.openSync(filepath, 'rs'); while(true) { // var bytesRead = fs.readSync(fdr, buffer,0,buffer.length); if(bytesRead>0) { // retHash.update(buffer.slice(0,bytesRead)); } else { // break; } } // fs.closeSync(fdr); // retInfo = stat.mtime.toISOString(); } // // base64 '='. return [retSize,retHash.digest('base64').replace(/=/g,''),retInfo]; } // module.exports = function(_basepath) { return _get_attributes('',_basepath); } })();
この機能の非同期実装。
// var fs = require('fs'); var path = require('path'); var crypto = require('crypto'); var async = require('async'); // . var qHashFile = async.queue(function (args, cb) { // var oHash = crypto.createHash('md5'); // var rs = fs.createReadStream(args.filePath); // - , rs.on('error', args.callback); // rs.on('data', function (data) { oHash.update(data); }); // rs.on('end', function () { // args.callback(null,oHash.digest('base64').replace(/=/g,'')); // cb(); }); }, 16); // // function getAttrDir(dirPath,callback) { //console.log("getAttrDir",dirPath); // fs.readdir(dirPath,function(err,items) { // , if (err) return callback(err); // var ret={ size : 0, // items:{} // }; var hashs=[]; // async.each(items, function(itemName,cb){ // getAttrItem(path.join(dirPath,itemName),function(err,attr){ // , if (err) return callback(err); // ret.size += attr.size; // ret.items[itemName] = attr; // hashs.push(attr.hash); // cb(); }); }, function(err) { // , if (err) return callback(err); // , // hashs.sort(); var oHash = crypto.createHash('md5'); hashs.forEach(function(hash) { oHash.update(hash); }); // ret.hash = oHash.digest('base64').replace(/=/g,''); // //console.log("getHashFile",dirPath,ret); callback(null,ret); }); }); } // function getAttrItem(itemPath,callback) { // fs.stat(itemPath, function(err,stat) { // , if (err) return callback(err); // if( stat.isDirectory() ) { // getAttrDir(itemPath,callback); } else { // qHashFile.push( { filePath : itemPath, callback : function(err,hash) { // callback(err,{ size : stat.size, // hash : hash, // mtime : stat.mtime.toISOString() // }); } }, function(err){ // , if(err) return callback(err); }); } }); } // module.exports = getAttrItem;
作業速度を測定するとき、非同期実装が常に優先されます。 ワークプロセスを高速化するために、pipe()関数を使用してスレッドリダイレクトを通じてチェックサム計算を行いました。 計算は、ファイルを読み取ることであり、計算はjavascripではなくノードのカーネルで行われます。 ただし、この手順はそれ自体を正当化しませんでした。 何らかの理由で、フォームのバリアント:
// var oHash = crypto.createHash('md5'); // var rs = fs.createReadStream(args.filePath); // - , rs.on('error', args.callback); // rs.on('data', function (data) { oHash.update(data); }); // rs.on('end', function () { // args.callback(null,oHash.digest('base64').replace(/=/g,'')); // cb(); });
私は常にオプションを破りました:
// var oHash = crypto.createHash('md5'); oHash.setEncoding('base64'); // var rs = fs.createReadStream(args.filePath); // rs.on('end', function() { // oHash.end(); // args.callback(null,oHash.read().replace(/=/g,'')); // cb(); }); // rs.pipe(oHash);
私は別の結果を望んでいましたが。 コメントでエラーが指摘されることを願っています。 また、チェックサムを計算するために同時に処理されるファイルの数に制限を設けました。 速度の大幅な向上は大きな値を与えませんが、特定のしきい値(私はそれを= 2.5千)で、ファイルを開くための新しいストリームを作成するときにプログラムがエラーでクラッシュしました:
events.js:72 throw er; // Unhandled 'error' event ^ Error: EMFILE, open '<- >'
2.変更されたファイルを特定する
このブロックはjavascriptで完全に実装されており、詳細に検討しません。 2つのオブジェクトを要素属性と比較するのに複雑なことはありません。 ディレクトリの変更があるかどうかを理解するには、ディレクトリの古い属性と新しい属性を比較するだけで十分です。 また、アイテムが古いバージョンではない場合、追加されたことを意味することを忘れないでください。つまり、変更されたアイテムのリストにあるはずです。
記事で言及されているすべての情報源はこちらです。 また、アーカイブには、時間測定テストを実行するためのtest.jsファイルがあります(ディスク上のパスに変更することを忘れないでください)。
次の記事では、ブロック番号3について検討します。そこで、データをパック/アンパックおよび暗号化/復号化するために、変換ストリームを使用します。 また、さまざまなバックアップストレージを操作するためのインターフェイスを作成します。
記事のpsUpdate。 コメントで与えられたコメントが考慮されました。
- 既にエスケープしていることが示されたため、jsコードのエスケープを削除しました。
- 非同期ライブラリを使用して書き換えられたコード。 それにより、コードは目に快適になりました(ただし、テストで私の初期バージョンを追い越すことはできませんでした:))。 ディレクトリエントリのリストを操作するためにasync.eachメソッドを使用しました。 ファイルのチェックサムを計算するためのasync.queueメソッドも同様です。
- 読み取り用のストリームを作成する際のエラー処理を追加しました。 これにより、ファイルを読み取るためにこれらの同じストリームが多数作成されたために、エラーが正確に発生することを確認できました。
- 現在、属性は、あいまいなインデックスを持つ配列の形式ではなく、名前を話すオブジェクト(サイズ、ハッシュ、アイテム、mdate)として返されます。
- コード自体は、コードの一貫性を高めようとしました。
すべてのソースコードへのリンク -これらは、記事で言及されている(言及されていない)すべてのソースコードです:バックアップデータ用NodeJSのスクリプト :開始 、 バックアップデータ用NodeJSのスクリプト:終了
メーカーのウェブサイトでヘルプ
プログラムをダウンロードするためのリンクは、最終的なプログラムへのリンクです。 ソースコードとは異なり、このアーカイブのデータは変更されます。 特に、FTPおよびyandex-diskのサポートを追加する予定です。 現在、ファイルシステムでの作業はストレージとしてサポートされています。 指定したフォルダーで保存が行われます。