バックアップデータのNodeJSスクリプト:終了

Backpupデータ用のスクリプトを作成するプロセスを引き続き説明します。 前の記事で、変更されたファイルのリストを定義する機能を作成しました(そして書き直しました)。 現時点では、変更されたデータのファイルを作成するプロセスについて説明します。



ファイルアーカイブの作成



この場合の変更されたデータは、変更されたファイルとディレクトリです(それらは追加/削除できます)。 したがって、変更されたデータのファイルは、本質的にアーカイブになります。 アーカイブを作成するとき、2つの方法があります-

  1. 各ファイルを個別にパックする
  2. すべてのファイルをまとめてパックする
  3. 混合戦略:すべてのファイルを1つの拡張子でパックできます


オプション2はあまり最適ではありません。アーカイブに.gifおよび.txtファイルを追加する必要がある場合、.gifをパックすると、その中のデータが既にパックされているという事実のためにサイズが増加します。テキストファイルは十分に圧縮されており、ファイルを解凍したままにしておくと、圧縮時に取得できるサイズを失います。

オプション1と3があります-そのうち、実装が簡単なので、最初のオプションを選択しました。

また、アーカイブがYandexディスクなどの外部リソースに保存され、データが盗まれるのを恐れないように、アーカイブでファイル暗号化を行います。 :)



アーカイブの概要を説明します







写真では、私の意見では、すべてが十分に明確であるため、アーカイブの形式を詳細に説明しません。



stream.Transformストリームを使用した暗号化とパッケージ化



暗号化とパッケージ化の方法に移りましょう。 これを行うには、 stream.Transformクラスを使用します。 このクラスのタイプはDuplex-つまり 彼はデータを読み書きできます。 _transform関数をオーバーライドして、必要に応じてデータを変換(暗号化/復号化、パック/アンパック)できます。

簡単にするために、XOR操作による暗号化クラスの例を作成します。

//    var stream = require("stream"); var util = require("util"); //    function XOR(options) { //   if (!(this instanceof XOR)) { return new XOR(options); } //     XOR this._byteXOR = 0xAB; //  stream.Transform stream.Transform.call(this, options); } util.inherits(XOR, stream.Transform); //    XOR.prototype._transform = function (chunk, enc, cb) { // /  for(var i=0;i<chunk.length;i++) { chunk[i] ^= this._byteXOR; } //   this.push(chunk, enc); //     cb(); }; //   module.exports = { //     createCipher : function(options) { return new XOR(options); }, //     createDecipher : function(options) { return new XOR(options); } };
      
      





すべてが非常に簡単です-stream.Transformクラスからクラス継承を作成し、次のパラメーターを持つ_transformメソッドを定義します

暗号化モジュールは2つの関数を返す必要があります

XOR操作は対称的であるため(つまり、復号化の場合、暗号化の場合と同じ操作を実行するだけで十分です)、私の場合、両方の関数が同じクラスを返します。

ファイル暗号化の例
pipe()スレッドリダイレクト機能を使用します
 //    var fs = require("fs"); var path = require("path"); //    var xor = require('./crypto/xor'); //   var inFile = __filename; var ipherFile = __filename+".xor"; var deipherFile = __filename+".in"; //     var rs = fs.createReadStream(inFile); //     var ts = xor.createCipher(); //     var ws = fs.createWriteStream(ipherFile); //  rs .pipe(ts) .pipe(ws) .on('finish', function () { //     var rs = fs.createReadStream(ipherFile); //     var ts = xor.createDecipher(); //     var ws = fs.createWriteStream(deipherFile); //  rs.pipe(ts).pipe(ws); });
      
      



暗号化のための自作クラスの例を挙げました。 プログラムでは、標準の暗号化クラス( 暗号ストリーム )とパッケージ化( zlibストリーム )を使用します。

パッケージ化機能は「packer」フォルダーに、暗号化機能は「crypto」フォルダーにあります。



アーカイブを作成するための既製のクラス
 //--    var stream = require("stream"); var util = require("util"); var path = require("path"); var async = require("async"); var fs = require("fs"); //    var FILENAME_HEADER = "header"; //    function requires(nameLib) { var res = {}; var fullpath = path.join(__dirname,nameLib); if( fs.existsSync(fullpath) ) { var files = fs.readdirSync(fullpath); for(var i in files) { var name = path.basename(files[i],".js"); //console.log(nameLib+">>","name\t",name); res[name] = require("./"+nameLib+"/"+name); } } return res; } //   ,     var _noPackExt=[ 'png','jpeg','jpg','gif','ico', 'docx','xlsx', 'mp4','avi', 'mp3','ogg', 'zip','rar','gz','7z','arj' ]; var noPackExt={}; for(var i in _noPackExt) { noPackExt["."+_noPackExt[i]] = 1; } //   function mkDir(pathDir) { //console.log(pathDir); var pathPrev = pathDir; var _dirs=[]; while(true) { var bname = path.basename(pathPrev); if(bname.length==0) break; _dirs.push(bname); pathPrev = path.dirname(pathPrev); } _dirs = _dirs.reverse(); //console.log("_dirs",_dirs,pathPrev); for(var i in _dirs) { pathPrev = path.join(pathPrev,_dirs[i]); //console.log(pathPrev,fs.existsSync(pathPrev)); if( !fs.existsSync(pathPrev) ) { try { fs.mkdirSync(pathPrev); } catch(ex) { } } } } //    var factoryCrypto = requires("crypto"); //    var factoryPacker = requires("packer"); //--         function streamBytesCount(options) { //   if (!(this instanceof streamBytesCount)) { return new streamBytesCount(options); } //    this._bytesCount = 0; //  stream.Transform stream.Transform.call(this, options); } util.inherits(streamBytesCount, stream.Transform); //    streamBytesCount.prototype._transform = function (chunk, enc, cb) { //   this._bytesCount += chunk.length; //   this.push(chunk, enc); //     cb(); }; //    streamBytesCount.prototype.bytesCount = function () { return this._bytesCount; }; //   module.exports = { //--      // arhPath -  // items -   // basePath -     pack : function(arhPath,basePath,items,opts,callback) { //   var cryptoID = opts.cryptoID ? opts.cryptoID : ""; //       var wsData = fs.createWriteStream(arhPath); //       var filepathHeader = arhPath+".dat"; var wsHeader = fs.createWriteStream(filepathHeader); //      function Number2Buffer(value,Nbytes) { //console.log("\tNumber2Buffer\t",value,"\t",Nbytes); //     var buf = new Buffer(Nbytes); //  switch(Nbytes) { case 1: buf.writeUInt8(value,0); break; case 2: buf.writeUInt16LE(value,0); break; case 4: buf.writeUInt32LE(value,0); break; case 8: { var hi = Math.round(value/256/256/256/256); var lo = value&0xFFFFFFFF; buf.writeUInt32LE(lo,0); buf.writeUInt32LE(hi,4); } break; } //    //console.log(value,Nbytes,buf,stream.path); // return buf; } //     function String2Buffer(str) { // var buf = new Buffer(str,'utf8'); return Buffer.concat([Number2Buffer(buf.length,2),buf]); } //   function _pushFile(filePath,item,cb,fdebug) { //   var packerID = ''; if( typeof(item.packerID)!='undefined' ) packerID = item.packerID; else { packerID = "node"; // :       ? var ext = path.extname(item.name).toLowerCase(); if( typeof(noPackExt[ext])!='undefined' ) { packerID = ''; } } //     var stream = fs.createReadStream(filePath); //  if( typeof(factoryPacker[packerID])!='undefined' ) { var sp = factoryPacker[packerID].createPacker({ params : opts.params ? opts.params : {} }); stream = stream.pipe(sp); } //  if( typeof(factoryCrypto[cryptoID])!='undefined' ) { var sc = factoryCrypto[cryptoID].createCipher({ password : opts.password ? opts.password : item.name, params : opts.params ? opts.params : {} }) stream = stream.pipe(sc); } //   var oBytesCount = new streamBytesCount; //    oBytesCount.on('end',function(err){ cb(err,oBytesCount.bytesCount(),packerID); }); // stream = stream.pipe(oBytesCount).pipe(wsData,{ end: false }); } //    async.eachSeries(items, function(item, cb) { //  : 0 -  / 1 -  var bufHeader = Number2Buffer(item.size<0 ? 1 : 0 ,1); //   bufHeader = Buffer.concat( [bufHeader,String2Buffer(item.name) ] ); //    (..   - 0) if(item.size>=0) { //   var filePath = path.join(basePath,item.name); //   _pushFile(filePath,item,function(err,sizePack,packerID){ //console.log("1"); //       bufHeader = Buffer.concat( [ bufHeader, Number2Buffer(item.size,8), //    Number2Buffer(sizePack,8), //    String2Buffer(packerID) //   ] ); //        wsHeader.write(bufHeader,cb); }); } else { //        wsHeader.write(bufHeader,cb); } }, function(err) { //  ,    if(err) return callback(err); //    wsHeader.end(); //    var packerID = "node"; //    var bufHeaderH = Buffer.concat([ //   String2Buffer(cryptoID), //    String2Buffer(packerID), //   Number2Buffer(items.length,8) ]); //     wsData.write(bufHeaderH,function(err) { //  ,   if(err) callback(err); //      _pushFile(filepathHeader,{ name : FILENAME_HEADER, packerID : packerID },function(err,sizePack,packerID){ // //console.log("header pack",sizePack,bufHeaderH.length); //       wsData.write(Number2Buffer(sizePack+bufHeaderH.length,8),function(err) { //   wsData.close(); //    wsData.on('close',function(){ //        fs.unlink(filepathHeader, callback); }); }); }); }); }); }, //  unpack : function(arhPath,fnIterator,opts,callback) { //     fs.stat(arhPath, function(err,stat) { //      var fdr = fs.openSync(arhPath, 'rs'); //      var position = stat.size - 8; //      N  function _readNumber(Nbytes) { var res=0; //    var buf = new Buffer(Nbytes); //console.log("position",position); position += fs.readSync(fdr, buf, 0, buf.length, position, buf.length); //   switch(Nbytes) { case 1: res = buf.readUInt8(0); break; case 2: res = buf.readUInt16LE(0); break; case 4: res = buf.readUInt32LE(0); break; case 8: { var lo = buf.readUInt32LE(0); var hi = buf.readUInt32LE(4); res = Math.round(hi*256*256*256*256)+lo; //console.log(hi,lo,res); } break; } // //console.log("\t_readNumber\t",res,"\t",Nbytes); // return res; } //     function _readString() { var res = ""; //    var len = _readNumber(2); //console.log("len string",len); //   if(len) { var buf = new Buffer(len); position += fs.readSync(fdr, buf, 0, buf.length, position, buf.length); res = buf.toString('utf8'); } //console.log("\t_readString\t",res); return res; } //      var sizeHeader = _readNumber(8); //console.log("sizeHeader",sizeHeader); //      position = stat.size - 8 - sizeHeader; //   var cryptoID = _readString(); //console.log("cryptoID",cryptoID); //    var packerID = _readString(); //console.log("packerID",packerID); //   var cnt = _readNumber(8); //console.log("cnt",cnt); //    sizeHeader -= (2+unescape(encodeURIComponent(cryptoID)).length+2+unescape(encodeURIComponent(packerID)).length+8); //   function _popFile(item,cb) { //console.log("_popFile",item.name,item.filepath); //   mkDir( path.dirname(item.filepath) ); //     var file = fs.createWriteStream(item.filepath); //      if(item.sizePack==0) { //   file.close(); } else { //   var stream = fs.createReadStream(arhPath,{ start : item.offset, end : (item.offset+item.sizePack-1) }); //  if( typeof(factoryCrypto[cryptoID])!='undefined' ) { var sc = factoryCrypto[cryptoID].createDecipher({ password : opts.password ? opts.password : item.name, params : opts.params ? opts.params : {} }) stream = stream.pipe(sc); } //  if( typeof(factoryPacker[item.packerID])!='undefined' ) { var sp = factoryPacker[item.packerID].createUnpacker({ params : opts.params ? opts.params : {} }); stream = stream.pipe(sp); } stream.pipe(file); } //    file.on('close', cb); } //      var filepathHeader = path.join(__dirname,"header.sb.extract.dat"); //   _popFile({ filepath : filepathHeader, name : FILENAME_HEADER, offset : (stat.size - 8 - sizeHeader), sizePack : sizeHeader, packerID : packerID },function(err){ //  ,   if(err) return callback(err); //     fdr = fs.openSync(filepathHeader, 'rs'); //      position = 0; //         var items=[] var offset = 0; while(cnt>0) { //    var item = { offset : offset }; //  item.typ = _readNumber(1); //  item.name = _readString(); //    if(item.typ==0) { //  item.size = _readNumber(8); //   item.sizePack = _readNumber(8); //   item.packerID = _readString(); //   offset += item.sizePack; } // //console.log("item",item); //    item.filepath = fnIterator(item); //       if( typeof(item.filepath)=='string' ) { //       items.push(item); } //      cnt--; } //console.log(items); //return; //    fs.unlink(filepathHeader, function(err){ //    (    4- ) async.eachLimit(items, 4, function(item, cb) { //   switch(item.typ) { case 0: //  { //   _popFile(item,cb); } break; case 1: //  { //   mkDir( item.filepath ); //    cb(); } break; } },callback); }); }); }); } };
      
      





ストレージを操作する



これは、ストレージリンク[ストレージタイプ ://] [ ストレージアドレス ]の外観です。

ストレージを操作するモジュールには2つのメソッドが含まれている必要があります

この場合、ストレージアドレスはメソッドに転送されます(つまり、ストレージのタイプはメソッドに転送されません)。
ファイルシステムに保存するFsモジュール
 //   fs ( ) var fs = require('fs'); var path = require('path'); //   module.exports = { //     put : function (url,filename,rs,callback) { //    var filepath = path.join(url,filename); //     var ws = fs.createWriteStream(filepath); ws.on('error',callback); //   rs.pipe(ws); //        ws.on('close',callback); }, //     get : function (url,filename,ws,callback) { //    var filepath = path.join(url,filename); //     var rs = fs.createReadStream(filepath); rs.on('error',callback); //   rs.pipe(ws); //        rs.on('close',callback); } };
      
      





すでに書かれたモジュールから最終プログラムを収集することは残っています。



最も一般的な間違い



そして、NodeJSでプログラムを作成する際の(私の経験上)最も一般的な間違いについて個別に書きたいと思います 。NodeJS の関数がASYNCHRONOUSであることを忘れないでください 。 特に、stream.close()ストリームメソッドが非同期であることを忘れていました。 だから私は閉じて、すぐにファイルを使い始めました。 時々それは機能しました(ファイルは閉じられました)、そして時にはそうではありません-そして、私はそれを操作するときにゼロの長さとエラーのファイルを受け取りました。 クローズイベントをキャッチして、そのイベントで作業を続ける必要がありました。 私の間違いを繰り返さないでください



すべてのソースコードへのリンク -これらは、記事で言及されている(言及されていない)すべてのソースコードです:バックアップデータ用NodeJSのスクリプト :開始バックアップデータ用NodeJSのスクリプト:終了

メーカーのウェブサイトでヘルプ

プログラムをダウンロードするためのリンクは、最終的なプログラムへのリンクです。 ソースコードとは異なり、このアーカイブのデータは変更されます。 特に、FTPおよびyandex-diskのサポートを追加する予定です。 現在、ファイルシステムでの作業はストレージとしてサポートされています。 指定したフォルダーで保存が行われます。



All Articles