遇到EOF时如何不停止阅读文件?

我正在尝试为Node.js实现一个例程,它允许打开一个文件,这个文件正在被其他进程附加到这个时候,然后在数据追加到文件时立即返回数据块。 可以认为它与tail -f UNIX命令类似,但是可以在块可用时立即执行,而不是随着时间的推移对轮询进行轮询。 另外,人们可以把它看作像使用套接字一样使用文件 – 期待on('data')不时触发,直到文件被显式closures。

在C语言中,如果我要实现这个function,我只需打开文件,input其文件描述符来select() (或者具有类似名称的任何替代函数),然后只读文件描述符标记为“可读”的块。 所以,当没有什么可读的时候,它就不会被读取,并且当文件附加了一些东西时,它就可以再次读取了。

我有点期待这样的行为在Javascript中的以下代码示例:

 function readThatFile(filename) { const stream = fs.createReadStream(filename, { flags: 'r', encoding: 'utf8', autoClose: false // I thought this would prevent file closing on EOF too }); stream.on('error', function(err) { // handle error }); stream.on('open', function(fd) { // save fd, so I can close it later }); stream.on('data', function(chunk) { // process chunk // fs.close() if I no longer need this file }); } 

然而,当遇到EOF时,这个代码示例只是保存,所以我不能等待新块到达。 当然,我可以使用fs.openfs.read来重新实现,但这有点击败了Node.js目的。 另外,我可以fs.watch()文件的变化,但它不会在networking上工作,我不喜欢重新打开文件的想法,而不是保持打开状态。

我试图做到这一点:

 const fd = fs.openSync(filename, 'r'); // sync for readability' sake const stream = net.Socket({ fd: fd, readable: true, writable: false }); 

但没有运气 – net.Socket不高兴,并引发TypeError: Unsupported fd type: FILE

那么,任何解决scheme?

我没有查看文件读取stream的内部,但可能不支持等待文件写入更多的数据。 但是, fs软件包绝对支持这个最基本的function。

为了解释拖尾是如何工作的,我写了一个有点hacky的tail函数,它会读取整个文件并为每一行调用一个callback(仅由\n分开),然后等待文件写入更多的行。 请注意,这样做的更有效的方法是拥有一个固定大小的行缓冲区,只是将字节混入(对于极长的行来说是特殊情况),而不是修改JavaScriptstring。

 var fs = require('fs'); function tail(path, callback) { var descriptor, bytes = 0, buffer = new Buffer(256), line = ''; function parse(err, bytesRead, buffer) { if (err) { callback(err, null); return; } // Keep track of the bytes we have consumed already. bytes += bytesRead; // Combine the buffered line with the new string data. line += buffer.toString('utf-8', 0, bytesRead); var i = 0, j; while ((j = line.indexOf('\n', i)) != -1) { // Callback with a single line at a time. callback(null, line.substring(i, j)); // Skip the newline character. i = j + 1; } // Only keep the unparsed string contents for next iteration. line = line.substr(i); // Keep reading in the next tick (avoids CPU hogging). process.nextTick(read); } function read() { var stat = fs.fstatSync(descriptor); if (stat.size <= bytes) { // We're currently at the end of the file. Check again in 500 ms. setTimeout(read, 500); return; } fs.read(descriptor, buffer, 0, buffer.length, bytes, parse); } fs.open(path, 'r', function (err, fd) { if (err) { callback(err, null); } else { descriptor = fd; read(); } }); return {close: function close(callback) { fs.close(descriptor, callback); }}; } // This will tail the system log on a Mac. var t = tail('/var/log/system.log', function (err, line) { console.log(err, line); }); // Unceremoniously close the file handle after one minute. setTimeout(t.close, 60000); 

所有这一切,你也应该尝试利用NPM社区。 随着一些search,我发现了尾stream包,可以做你想做的事情,用stream。

以前的答案提到了tail-stream的方法,它使用fs.watch,fs.read和fs.stat来创buildstream式传输文件内容的效果。 你可以在这里看到这个代码。

另一种可能更冒险的做法可能是通过产生一个subprocess来使用tail。 这当然是有目标平台上必须存在的尾巴的限制,但节点的优势之一是通过产卵,甚至在Windows上使用它进行asynchronous系统开发,您可以执行一个像msysgit或cygwin备用shell中的节点来获取访问尾巴实用程序。

代码为:

 var spawn = require('child_process').spawn; var child = spawn('tail', ['-f', 'my.log']); child.stdout.on('data', function (data) { console.log('tail output: ' + data); } ); child.stderr.on('data', function (data) { console.log('err data: ' + data); } ); 

你要做的是一个FIFO文件(先入先出的首字母缩略词),正如你所说的那样工作就像一个套接字。

有一个node.js模块可以让你使用fifo文件。

我不知道你想要什么,但有更好的方法来处理Node.js上的套接字。 尝试使用socket.io 。

你也可以看看以前的这个问题: 使用Node.js实时读取一个文件

更新1

我不熟悉任何模块,将会使用常规文件而不是套接字types。 但正如你所说,你可以使用tail -f来实现:

 // filename must exist at the time of running the script var filename = 'somefile.txt'; var spawn = require('child_process').spawn; var tail = spawn('tail', ['-f', filename]); tail.stdout.on('data', function (data) { data = data.toString().replace(/^[\s]+/i,'').replace(/[\s]+$/i,''); console.log(data); }); 

然后从命令行尝试echo someline > somefile.txt并在控制台上观看。

你可能也想看看这个: https : //github.com/layerssss/node-tailer