Nodejs 循环调用fs模块进行写入文件操作报错的问题

Nodejs 循环调用fs模块进行写入文件操作报错的问题

我做了一个错误日志记录的功能,代码如下:

fs.open(filename, 'a', function(error, fd){
if(!error){
	fs.write(fd, logs);
	fs.close(fd);
	logs = ‘’;
}
});

由于循环写入日志,造成不稳定,执行一段时间之后就会报错如下:

fs.js:75
  throw err;  // Forgot a callback but don't know where? Use NODE_DEBUG=fs
        ^
Error: EBADF, write

请问该怎么处理,或者有其他什么好的方法,谢谢!


5 回复

Node.js 循环调用 fs 模块进行写入文件操作报错的问题

在使用 Node.js 编写一个错误日志记录功能时,你可能会遇到因为频繁地循环写入日志文件而导致的错误。以下是一个常见的问题及其解决方案。

问题描述

你编写了如下代码来循环写入日志文件:

fs.open(filename, 'a', function(error, fd) {
    if (!error) {
        fs.write(fd, logs);
        fs.close(fd);
        logs = '';
    }
});

但是,当你运行这段代码一段时间后,会遇到如下的错误:

fs.js:75
  throw err;  // Forgot a callback but don't know where? Use NODE_DEBUG=fs
        ^
Error: EBADF, write

这个错误通常表示文件描述符(file descriptor)已经被关闭或无效。

原因分析

错误 EBADF 表示文件描述符无效。这可能是因为在每次循环中都重新打开并关闭文件,导致文件描述符在某些情况下变得无效。此外,频繁地打开和关闭文件也可能导致性能问题。

解决方案

为了避免这个问题,可以考虑将文件打开的操作移到循环之外,并且使用异步写入操作来确保每次写入完成后再进行下一次写入。以下是改进后的代码示例:

const fs = require('fs');

let logs = '';

// 打开文件
fs.open('error.log', 'a', (err, fd) => {
    if (err) {
        console.error('Failed to open file:', err);
        return;
    }

    function logError() {
        if (logs !== '') {
            fs.write(fd, logs + '\n', (writeErr) => {
                if (writeErr) {
                    console.error('Failed to write to file:', writeErr);
                } else {
                    logs = '';  // 清空日志
                }
                // 如果还有日志需要写入,则递归调用
                if (logs !== '') {
                    logError();
                }
            });
        }
    }

    // 初始写入日志
    logError();

    // 模拟新的日志产生
    setInterval(() => {
        logs += new Date().toISOString() + ': New error occurred';
        logError();  // 调用写入函数
    }, 1000);  // 每秒生成一条新日志
});

// 确保在程序退出前关闭文件
process.on('SIGINT', () => {
    fs.close(fd, (closeErr) => {
        if (closeErr) {
            console.error('Failed to close file:', closeErr);
        } else {
            process.exit(0);
        }
    });
});

解释

  1. 文件打开:将文件打开的操作移到循环之外,避免频繁打开和关闭文件。
  2. 异步写入:使用回调函数确保每次写入操作完成后才进行下一次写入。
  3. 递归调用:如果还有日志需要写入,则递归调用 logError 函数。
  4. 清理操作:在程序退出前确保文件被正确关闭。

通过这种方式,你可以有效地避免 EBADF 错误,并确保日志文件能够稳定地写入。


fs的write和close都是异步的,你要改成回调方式。

或者,fs有个appendfile可以直接用。

fs.appendFile('message.txt', 'data to append', function (err) {
  if (err) throw err;
  console.log('The "data to append" was appended to file!');
});

Node 现在都有 fs.open 了, 真是不习惯啊

嗯,用appendFile可以的!谢谢!

在使用Node.js循环调用fs模块进行文件写入操作时,可能会遇到EBADF(Bad file descriptor)错误。这通常是因为文件描述符被意外关闭或重用了。为了避免这种情况,可以使用fs.createWriteStream()来创建一个可写的流,并利用流的特性来连续写入数据。

示例代码

const fs = require('fs');
const path = require('path');

const filename = path.join(__dirname, 'logs.txt');

// 创建一个可写的流
const logStream = fs.createWriteStream(filename, { flags: 'a' });

setInterval(() => {
    const logs = `New log entry at ${new Date().toISOString()}\n`;
    logStream.write(logs);
}, 1000); // 每秒写入一次日志

解释

  1. 使用fs.createWriteStream

    • 创建一个持久化的写入流,这样即使循环频繁地写入,也不会导致文件描述符被关闭。
    • { flags: 'a' }表示以追加模式打开文件,这样每次写入都会追加到文件末尾。
  2. 定时器setInterval

    • 使用setInterval每秒触发一次日志写入操作,模拟频繁写入的场景。
  3. 避免多次打开文件

    • 使用单个流对象来持续写入数据,而不是每次都去打开文件,这样可以避免文件描述符被意外关闭的问题。

通过这种方式,你可以确保即使在高频率的写入操作中也能稳定地记录日志,而不会遇到EBADF错误。

回到顶部