ファイルアーカイブの作成
この場合の変更されたデータは、変更されたファイルとディレクトリです(それらは追加/削除できます)。 したがって、変更されたデータのファイルは、本質的にアーカイブになります。 アーカイブを作成するとき、2つの方法があります-
- 各ファイルを個別にパックする
- すべてのファイルをまとめてパックする
- 混合戦略:すべてのファイルを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メソッドを定義します
- chunk-変換するデータのチャンク
- enc-エンコード
- cbはコールバック関数です。 処理が完了すると呼び出されます。
- createCipher-暗号化用のクラスを作成する関数
- createDecipher-復号化用のクラスを作成する関数
ファイル暗号化の例
暗号化のための自作クラスの例を挙げました。 プログラムでは、標準の暗号化クラス( 暗号化ストリーム )とパッケージ化( zlibストリーム )を使用します。
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); });
パッケージ化機能は「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つのメソッドが含まれている必要があります
- put ( url 、 filename 、 rs 、 コールバック(err) )-rsデータストリームをストレージに書き込むためのメソッド
- url-ストレージアドレス
- filename-ファイル名
- rs-データを読み取るためのストリーム
- コールバック(err) -コールバック関数。 操作が完了した後に呼び出されます。
- get ( url 、 filename 、 ws 、 callback(err) )-リポジトリからwsストリームにファイルを読み込むためのメソッド
- url-ストレージアドレス
- filename-ファイル名
- ws-データを書き込むためのストリーム
- コールバック(err) -コールバック関数。 操作が完了した後に呼び出されます。
ファイルシステムに保存する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のサポートを追加する予定です。 現在、ファイルシステムでの作業はストレージとしてサポートされています。 指定したフォルダーで保存が行われます。