我正在尝试为Node.js实现一个例程,该例程允许用户打开一个文件,此时正被其他一些进程附加,然后在附加到的时候立即返回数据块文件。它可以被认为类似于tail -f
UNIX命令,但是当块可用时立即执行,而不是轮询一段时间内的更改。或者,人们可以将其视为与使用套接字一样处理文件 - 期望on('data')
不时触发,直到明确关闭文件。
在C land中,如果我要实现它,我只需打开文件,将其文件描述符提供给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()
提交更改文件,但它不能通过网络工作,我不喜欢一直重新打开文件的想法,而不是仅仅保持打开。
我试过这样做:
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
。
那么,有什么解决方案吗?
答案 0 :(得分:0)
我没有查看文件读取流的内部,但是它们可能不支持等待文件写入更多数据。但是,fs
包肯定支持它的最基本功能。
为了解释尾部如何工作,我编写了一个有点hacky tail
函数,它将读取整个文件并为每一行调用一个回调(仅由\n
分隔),然后等待该文件有更多的行写入它。请注意,更有效的方法是使用固定大小的行缓冲区,只需将字节拖入其中(特殊情况下需要非常长的行),而不是修改JavaScript字符串。
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社区。通过一些搜索,我找到了tail-stream包可以做你想要的,用流。
答案 1 :(得分:0)
之前的答案提到tail-stream的方法,它使用fs.watch,fs.read和fs.stat一起创建流式传输文件内容的效果。您可以在行动here中看到该代码。
另一种,也许更黑客的方法可能是通过使用它生成子进程来使用tail。这当然伴随着tail必须存在于目标平台上的限制,但节点的优势之一是使用它来通过spawn甚至在windows上进行异步系统开发,你可以在像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);
}
);
答案 2 :(得分:0)
因此,似乎人们已经在寻找这个问题的答案已有五年了,而关于该主题的答案还没有。
简而言之:您不能。尤其是在Node.js中,您根本无法。
长答案:没有什么理由。
首先,在这方面,POSIX标准clarifies select()
behavior如下:
与常规文件关联的文件描述符应始终为true以便准备读取,准备写入以及出现错误情况。
因此,select()
无助于检测超出文件末尾的写入。
常规文件应始终以TRUE进行读写。
我不能肯定地使用epoll()
,因为它不是标准的,您必须阅读quite lengthy implementation,但我认为它是相似的。
由于libuv是Node.js实现的核心,因此它使用read()
, pread()
and preadv()
in its uv__fs_read()
,在文件末尾调用时两者都不会阻塞,因此遇到EOF时,它将始终返回空缓冲区。所以,这里也没有运气。
因此,总结一下,如果需要这种功能,那么您的设计肯定有问题,应该对其进行修改。
答案 3 :(得分:-1)
你要做的是一个FIFO文件(先入先出的首字母缩写),正如你所说的那样就像一个插座。
有node.js module that allows you to work with fifo个文件。
我不知道你想要什么,但有更好的方法来处理node.js上的套接字。请改为socket.io。
您还可以查看上一个问题: Reading a file in real-time using Node.js
更新1
我不熟悉使用常规文件而不是套接字类型的任何模块。但正如你所说,你可以使用$c = 0;
foreach ($sale['fm'] as $key => $value) {
//Here you have the related tax
$tax = $taxes[$c];
$i=0;
$g=count($value['base']);
while($i < $g) {
echo($sale['fm'][$key]['base'][$i]."<br />");
$i++;
}
$c++;
}
来做这个伎俩:
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);
});
并在控制台上观看。
您可能还希望看一下:https://github.com/layerssss/node-tailer