Node.js中fsモジュール使用例--ファイル読み書き

19096 ワード

Node.jsファイルI/Oはシンプルパッケージの標準POSIX関数から提供される[1]です.fs(File System)はNode.jsの内蔵モジュールです.
const fs = require('fs');
このモジュールを使用します fsモジュールのすべてのAPIは、非同期の形態を有する.非同期形式は常に最終パラメータとしてフィードバックを完了する.フィードバックを完了するパラメータは具体的な方法に依存するが、最初のパラメータは常に異常を残している.操作が成功すれば、最初のパラメータはnullまたはundefinedになります.同期形式を使うと、どの異常もすぐに投げ出されます.try/catchを使用して異常を処理したり、上に泡を発生させたりすることができます.非同期APIを使用すると、関数の呼び出し順序が保証されません.以下のコードの中で、ファイル名を変更し、fs.statを使って名前変更操作結果を確認します.非同期APIを使用すると、fs.statは、fs.renameを起動する前に実行されるかもしれない[1].
fs.rename('/tmp/hello', '/tmp/world', function(err){
  if (err) throw err;
  console.log('renamed complete');
});
fs.stat('/tmp/world', function(err, stats){
  if (err) throw err;
  console.log(`stats: ${JSON.stringify(stats)}`);
});
の正しいやり方は、一連のコールバック関数を使用してAPI呼び出し順序を保証することである.
fs.rename('/tmp/hello', '/tmp/world', function(err){
  if (err) throw err;
  fs.stat('/tmp/world', function(err, stats){
    if (err) throw err;
    console.log(`stats: ${JSON.stringify(stats)}`);
  });
});
ファイルの読み書き
readFileを使ってファイルのサンプルを読む[2]
var fs = require('fs'); 

var readFileName = './foo.txt';  
var writeFileName = './target.txt';  

/** Using the readFile API - Asynchronous */
fs.readFile( readFileName, "utf8", function(err, data){
  if ( err ){ throw err;}
  console.log("Reading file asynchronously");
  console.log(data);
});
readFileSyncを使ってファイルのサンプルを読む[2]
var fs = require('fs'); 

var readFileName = './foo.txt';  
var writeFileName = './target.txt';  

/** Using the readFile API - Asynchronous */
console.log("Reading file synchronously");
var fileData = fs.readFileSync(  readFileName, "utf8");
console.log(fileData);
ReadStreamを使ってファイルのサンプルを読む[2]
var fs = require('fs'); 

var readFileName = './foo.txt';  
var writeFileName = './target.txt';  

/** Reading file using ReadStream API */
//Creating a stream out of the file
var readStreamObject = fs.createReadStream( readFileName, { flags: 'r',
  encoding:"utf8",
  fd: null,
  mode: 0666,
  autoClose: true
});

//Setting up event handlers on the stream object
//readable - this event is fired when data can be read from stream
readStreamObject.on('readable', function(){
  console.log("*** Reading from file using ReadStream");
});


//data - this event is fired when data is available to be read from stream
readStreamObject.on('data', function(data){
  console.log(data);
});
writeFileSyncを使ってファイルのサンプルを作成する[2]
var fs = require('fs'); 

var readFileName = './foo.txt';  
var writeFileName = './target.txt';  

fs.writeFileSync( writeFileName, "Writing to a file synchronously from node.js", {"encoding":'utf8'});

console.log("*** File written successfully");

//Now reading the same file to confirm data written
fs.readFile(  writeFileName, "utf8", function(err, data){
  if ( err ){ throw err;}
  console.log("*** Reading just written file");
  console.log(data);
});
writeFileを使ってファイルのサンプルを作成する[2]
var fs = require('fs'); 

var readFileName = './foo.txt';  
var writeFileName = './target.txt';  

fs.writeFile(  writeFileName, "Writing to a file from node.js", {"encoding":'utf8'}, function(err){
  if ( err ) { throw err; }
  console.log("*** File written successfully");
  //Now reading the same file to confirm data written
  fs.readFile( writeFileName, "utf8", function(err, data){
    if ( err ){ throw err;}
    console.log("*** Reading just written file");
    console.log(data);
  });

});
何回も同じファイルにfs.writeを使っていますが、まだ返事を待っていないので、安全ではありません.fs.reate WriteStream[1]を使うことをオススメします. .
createWriteStreamを使ってファイルのサンプルを作成する[2]
var fs = require('fs'); 

var readFileName = './foo.txt';  
var writeFileName = './target.txt';  


//Create a stream with the required path
var writeStreamObject = fs.createWriteStream( writeFileName );

//write to the stream using the API
writeStreamObject.write("Writing to a file using WriteStream", "utf8");

//Now read the same file to verify that the contents have been successfully written
fs.readFile( writeFileName, "utf8", function(err, data){
  if ( err ){ throw err;}
  console.log("*** Reading the recently written file");
  console.log(data);
});
ファイルコピー
ファイルコピー例[3]
var fs = require('fs'),
    path = require('path'),
    out = process.stdout;
var stat
var filePath = './foo.mkv';

var readStream = fs.createReadStream(filePath);
var writeStream = fs.createWriteStream('file.mkv');

try {
  stat = fs.statSync(filePath);
  console.log("File exists.");
}
catch (e) {
    if (e.code == 'ENOENT') {  
      console.log("File does not exist.");
      //return false;
    }
  console.log("Exception fs.statSync (" + filePath + "): " + e);
  // console.log(filePath + " does not exist.");
}

var totalSize = stat.size;
var passedLength = 0;
var lastSize = 0;
var startTime = Date.now();

readStream.on('data', function(chunk) {

    passedLength += chunk.length;

    if (writeStream.write(chunk) === false) {
        readStream.pause();
    }
});

readStream.on('end', function() {
    writeStream.end();
});

writeStream.on('drain', function() {
    readStream.resume();
});

setTimeout(function show() {
    var percent = Math.ceil((passedLength / totalSize) * 100);
    var size = Math.ceil(passedLength / 1000000);
    var diff = size - lastSize;
    lastSize = size;
    out.clearLine();
    out.cursorTo(0);
    out.write('   ' + size + 'MB, ' + percent + '%,   :' + diff * 2 + 'MB/s');
    if (passedLength < totalSize) {
        setTimeout(show, 500);
    } else {
        var endTime = Date.now();
        console.log();
        console.log('   :' + (endTime - startTime) / 1000 + ' 。');
    }
}, 500);
Synch ronous Copy
コード例[7]
copyFileSync = (srcFile, destFile) 
function copyFileSync(srcFile, destFile) {
  var BUF_LENGTH = 64*1024  
  var buff = new Buffer(BUF_LENGTH)  
  var fdr = fs.openSync(srcFile, 'r')  
  var fdw = fs.openSync(destFile, 'w') 
  var bytesRead = 1  
  var pos = 0  

  while( bytesRead > 0 )
  { 
    bytesRead = fs.readSync(fdr, buff, 0, BUF_LENGTH, pos)  
    fs.writeSync(fdw,buff,0,bytesRead)  
    pos += bytesRead  
  }
  fs.closeSync(fdr);  
  fs.closeSync(fdw);  
}
Results of testing influnce of the buffer size for time consumpation shows that there is a lower limitation of the parameter. For one file about 300 MB,the coping processs with various buffer sizes,which over 2 KB、take roughly simiar time. If the buffer is lower than 2 KB、however、time computation would increase significantly. 
Aynchronous Copy
stream version in one line[8]
var fs = require('fs');  
  
fs.createReadStream('test.log').pipe(fs.createWriteStream('newLog.log')); 
stream version with errors handling provided by Mike Shilling[8]
function copyFile(source, target, cb) {  
  var cbCalled = false;  
  
  var rd = fs.createReadStream(source);  
  rd.on("error", function(err) {  
    done(err);  
  });  
  var wr = fs.createWriteStream(target);  
  wr.on("error", function(err) {  
    done(err);  
  });  
  wr.on("close", function(ex) {  
    done();  
  });  
  rd.pipe(wr);  
  
  function done(err) {  
    if (!cbCalled) {  
      cb(err);  
      cbCalled = true;  
    }  
  }  
}  
stream version with errors handling presented by Jens Hauke[8]
function copyFile(source, target, cb) {  
  var cbCalled = false;  
  
  var rd = fs.createReadStream(source);  
  rd.on("error", done);  
  
  var wr = fs.createWriteStream(target);  
  wr.on("error", done);  
  wr.on("close", function(ex) {  
    done();  
  });  
  rd.pipe(wr);  
  
  function done(err) {  
    if (!cbCalled) {  
      cb(err);  
      cbCalled = true;  
    }  
  }  
}  
大きなファイルの読み書き
多くの場合、ファイルを読むスピードはファイルを書くより速いです.そうすると、大量のデータがメモリに蓄積され、読みたいファイルが大きいと、メモリを使う量が多すぎて、Node.jsプロセスが崩壊することになります.読み取り速度が私たちの期待した値を超えた場合は、PAuse()を実行して一旦停止し、タイミングが合ったら再度reume()を実行して[5]を再開します.
var util = require('util');
var events = require('events');
var fs = require('fs');

//      M      
var inputFile = '/Volumes/foo.txt';


function ReadStreamThrottle (stream, speed) {
  this._stream = stream;
  this._readBytes = 0;
  this._speed = speed;
  this._ended = false;
  this._readBytesSecond = 0;
  this._lastTimestamp = Date.now();
  this._paused = false;
  var self = this;

  //         
  function isTooFast () {
    var t = (Date.now() - self._lastTimestamp) / 1000;
    var bps = self._readBytesSecond / t;
    return bps > speed;
  }

  //           
  function checkSpeed () {
    if (isTooFast()) {
      self.pause();
      //                   
      var tid = setInterval(function () {
        if (!isTooFast()) {
          clearInterval(tid);
          self.resume();
        }
      }, 100);
    } else {
      self.resume();
    }
  }

  stream.on('data', function (chunk) {
    self._readBytes += chunk.length;
    self._readBytesSecond += chunk.length;
    self.emit('data', chunk);
    checkSpeed();
  });

  stream.on('end', function () {
    self._ended = true;
    self.emit('end');
  });
}

util.inherits(ReadStreamThrottle, events.EventEmitter);

ReadStreamThrottle.prototype.pause = function () {
  this._paused = true;
  this._stream.pause();
};

ReadStreamThrottle.prototype.resume = function () {
  this._paused = false;
  this._stream.resume();
};


//     ,        10MB/S
var MB = 1024 * 1024;
var s = new ReadStreamThrottle(fs.createReadStream(inputFile), MB * 10);
var bytes = 0;
var t = Date.now();
s.on('data', function (c) {
  bytes += c.length;
  var spent = (Date.now() - t) / 1000;
  console.log('read %s bytes, speed: %sMB/S', bytes, (bytes / MB / spent).toFixed(2));
});
s.on('end', function () {
  console.log('end. total %s bytes', bytes);
});
ファイルアップロード
「RFC 1867-Form-based File Upload in HTML」プロトコルに従って、Node.jsを使ってファイルアップロードを実現します.
ブラウザでフォームを使ってファイルをアップロードするときに使うパケットのフォーマットは以下の通りです.
POSThttp://www.foo.com/ HTTP/1.1
Host: www.foo.com
Content-Length: 199
Content-Type: multipart/form-data; boundary=----WebKitFormBoundarywr3X7sXBYQQ4ZF5G
------WebKitFormBoundarywr3X7sXBYQQ4ZF5G
Content-Disposition: form-data; name="myfile"; filename="upload.txt"
Content-Type: text/plain
hello world
------WebKitFormBoundarywr3X7sXBYQQ4ZF5G--
サンプルコード[9]
const http = require('http');
const fs = require('fs');


//      
var boundaryKey = '----WebKitFormBoundaryjLVkbqXtIi0YGpaB'; 

var currentDir = __dirname + '/';
var sourceFileName = 'BMW5_torsion.bdf'
var filePath = currentDir + sourceFileName;

// checking file
try {  
  stat = fs.statSync(filePath);  
  console.log("File exists.");  
}  
catch (e) {  
    if (e.code == 'ENOENT') {    
      console.log("File does not exist.");  
      //return false;  
    }  
  console.log("Exception fs.statSync (" + filePath + "): " + e);  
  // console.log(filePath + " does not exist.");  
}


// 
var options = {
    hostname: 'localhost',
    port: 80,
    path: '/fileupload',
    method: 'POST'
}


//           

fs.readFile( filePath, function (err, data) {
    //       
    var payload = '--' + boundaryKey + '\r
' ; payload += 'Content-Disposition:form-data; name="myfile"; filename="'+ sourceFileName + '"\r
' ; payload += 'Content-Type:text/plain\r
\r
'; payload += data; payload += '\r
--' + boundaryKey + '--'; // var req = http.request(options, function (res) { res.setEncoding('utf8'); res.on('data', function (chunk) { console.log('body:' + chunk); }); }); req.on('error', function(e) { console.error("error:"+e); }); // boundary、 req.setHeader('Content-Type', 'multipart/form-data; boundary='+boundaryKey+''); req.setHeader('Content-Length', Buffer.byteLength(payload, 'utf8')); req.write(payload); req.end(); });
ファイルアップロードのサンプル[4]
var urlparse = require('url').parse
  , http = require('http')
  , fs = require('fs');

function upload(url, uploadfile, callback) {
    var urlinfo = urlparse(url);
    var options = {
        method: 'POST',
        host: urlinfo.host,
        path: urlinfo.pathname
    };
    if(urlinfo.port) {
        options.port = urlinfo.port;
    }
    if(urlinfo.search) {
        options.path += urlinfo.search;
    }
    var req = http.request(options, function(res) {
        var chunks = [], length = 0;
        res.on('data', function(chunk) {
            length += chunk.length;
            chunks.push(chunk);
        });
        res.on('end', function() {
            var buffer = new Buffer(length);
            // delay copy
            for(var i = 0, pos = 0, size = chunks.length; i < size; i++) {
                chunks[i].copy(buffer, pos);
                pos += chunks[i].length;
            }
            res.body = buffer;
            callback(null, res);
        });
    });
    var readstream = fs.createReadStream(uploadfile);
    readstream.on('data', function(chunk) {
        console.log('write', chunk.length);
        req.write(chunk);
    });
    readstream.on('end', function() {
        req.end();
    });
};

upload('http://weibo.com/', '/tmp/bigfile.pdf', function(err, res) {
    console.log(res.statusCode, res.headers);
});
ファイルのダウンロード
大ファイルのダウンロード例[4]
var urlparse = require('url').parse
var http = require('http')
var fs = require('fs'); 

function download(url, savefile, callback) {
    console.log('download', url, 'to', savefile)
    var urlinfo = urlparse(url);
    var options = {
        method: 'GET',
        host: urlinfo.host,
        path: urlinfo.pathname
    };
    if(urlinfo.port) {
        options.port = urlinfo.port;
    }
    if(urlinfo.search) {
        options.path += urlinfo.search;
    }
    var req = http.request(options, function(res) {
        var writestream = fs.createWriteStream(savefile);
        writestream.on('close', function() {
            callback(null, res);
        });
        res.pipe(writestream);
    });
    req.end();
};

download('http://web.mit.edu/cocosci/Papers/sci_reprint.pdf', '/temp/isomap.pdf', function(err, res) {
    console.log(res.statusCode, res.headers);
});
大きなファイルのダウンロードと断点更新
大きなファイルのダウンロードと断点継続の基本原理は、ファイルのダウンロードが切断された後です.クライアントがサーバ側に要求を続けると、http要求のヘッダファイルには一つのパラメータ「Range」が追加され、現在ダウンロードされているファイルが切断されている位置を表示します.
function Transfer(req, resp) {
	this.req = req;
	this.resp = resp;
}
/**
 * [@description](/user/description)          
 * [@param](/user/param) {string} Range   http         ,      undefined,  (range: bytes=232323-)
 * [@return](/user/return) {integer} startPos       
 */
Transfer.prototype._calStartPosition = function(Range) {
	var startPos = 0;
	if( typeof Range != 'undefined') {
		var startPosMatch = /^bytes=([0-9]+)-$/.exec(Range);
		startPos = Number(startPosMatch[1]);
	}
	return startPos;
}
/**
 * [@description](/user/description)      
 * [@param](/user/param) {object} Config        (                )
 */
Transfer.prototype._configHeader = function(Config) {
	var startPos = Config.startPos, 
		fileSize = Config.fileSize,
		resp = this.resp;
	//   startPos 0,     0     ,           。
	if(startPos == 0) {
		resp.setHeader('Accept-Range', 'bytes');
	} else {
		resp.setHeader('Content-Range', 'bytes ' + startPos + '-' + (fileSize - 1) + '/' + fileSize);
	}
	resp.writeHead(206, 'Partial Content', {
		'Content-Type' : 'application/octet-stream',
	});
}
/**
 * [@description](/user/description)        
 * [@param](/user/param) {string} filePath
 * [@param](/user/param) {function} down          
 */
Transfer.prototype._init = function(filePath, down) {
	var config = {};
	var self = this;
	fs.stat(filePath, function(error, state) {
		if(error)
			throw error;

		config.fileSize = state.size;
		var range = self.req.headers.range;
		config.startPos = self._calStartPosition(range);
		self.config = config;
		self._configHeader(config);
		down();
	});
}
/**
 * [@description](/user/description)         ,   
 * [@param](/user/param) {string} filePath     
 */
Transfer.prototype.Download = function(filePath) {
	var self = this;
	path.exists(filePath, function(exist) {
		if(exist) {
			self._init(filePath, function() {
				var config = self.config
					resp = self.resp;
				fReadStream = fs.createReadStream(filePath, {
					encoding : 'binary',
					bufferSize : 1024 * 1024,
					start : config.startPos,
					end : config.fileSize
				});
				fReadStream.on('data', function(chunk) {
					resp.write(chunk, 'binary');
				});
				fReadStream.on('end', function() {
					resp.end();
				});
			});
		} else {
			console.log('     !');
			return;
		}
	});
}


var fs = require('fs')
http = require('http')
path = require('path');
var server = http.createServer(function(req, resp) {
	var transfer = new Transfer(req, resp);
	var filePath = '/Users/xukai/Downloads/love.rmvb';
	transfer.Download(filePath);
});
server.listen('8000');
 isfile()
Node.js中fsモジュールのisfile()関数使用例  
var fs = require('fs');

var path = '.\foo.txt';

//    
fs.statSync( path ).isFile() ;


//    
fs.stat( path, function(s){
    callback(s.isFile() );
});
参考文献
[1]http://nodejs.cn/api/fs  [2]http://javabeat.net/nodejs-read-write-file/ [3]https://segmentfault.com/a/1190000000519006[4]http://www.cnblogs.com/fengmk2/archive/2011/08/16/2140460.html[5]http://morning.work/page/2015-07/read_and_write_big_file_同前nodejs.[6]https://cnodejs.org/topic/4f5b47c42373009b5c04e9cb[7]http://procbits.com/2011/11/15/synchronous-file-copy-in-node-js[8]http://stackoverflow.com/questions/11293857/fastest-way-to-copy-file-in-node-js[9]http://www.aichengxu.com/javascript/24609788.htm