遇到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.open
和fs.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