请教各位一个关于Nodejs中fs的writeFile方法的问题

请教各位一个关于Nodejs中fs的writeFile方法的问题

主要经过是这样的,在一个项目中需要动态写一些文件,大概几十个吧,每个文件的内容在10KB左右,都不大,写文件用了异步的方法,在一个循环里面写,所以几乎都是在同一时间写的,这时候就会报一个错:EMFILE,网上找了一下说是linux不允许同时打开超过1024个文件,但是我同时顶多也打开了几十个而已,写的内容也不多,于是单独去测试了一下,在一个循环里面,用异步方法写文件,发现最多同时只能写248个文件,再多就不会写下去了,请问下这是什么原因?跟操作系统有关么?我是OSX 10.7,nodejs版本是0.8.XXX


5 回复

教会各位一个关于Node.js中fs.writeFile方法的问题

问题描述

在项目中,我需要动态创建并写入几十个文件,每个文件大小大约为10KB。使用的是异步方法,并且这些文件写操作是在一个循环中完成的。然而,当我尝试写入超过248个文件时,程序会抛出一个EMFILE错误。这似乎与Linux系统限制有关,但我在Mac OS X 10.7上遇到同样的问题,Node.js版本为0.8.XXX

原因分析

EMFILE错误通常表示程序试图同时打开的文件数量超过了系统的限制。尽管你认为自己没有达到该限制,但在Node.js环境中,每个fs.writeFile调用实际上都会打开文件、写入数据然后关闭文件。因此,即使你在循环中异步地写入文件,如果文件数量过多,可能会导致短时间内打开的文件句柄数量超过系统或Node.js本身的限制。

解决方案

  1. 增加文件描述符限制: 可以通过修改系统配置来增加允许同时打开的文件描述符数量。对于Mac OS X,可以编辑/etc/security/limits.conf文件(可能需要root权限),或者在启动Node.js应用前设置环境变量:

    ulimit -n 4096
    
  2. 优化文件写入逻辑: 在Node.js中,可以采用批量处理的方式,而不是一次性写入所有文件。这样可以避免瞬间打开大量文件句柄。例如,可以将文件写入任务分批执行:

    const fs = require('fs');
    const path = require('path');
    
    function writeFileBatch(files, batchSize) {
      let i = 0;
      function writeNext() {
        if (i < files.length) {
          const file = files[i];
          fs.writeFile(file.path, file.content, err => {
            if (err) {
              console.error(`Failed to write ${file.path}:`, err);
            }
            i++;
            setTimeout(writeNext, 0); // 使用setTimeout让当前事件循环结束
          });
        }
      }
    
      writeNext();
    }
    
    const files = Array.from({ length: 300 }, (_, index) => ({
      path: path.join(__dirname, `file${index}.txt`),
      content: 'This is some content for the file.'
    }));
    
    writeFileBatch(files, 25);
    

    这段代码将文件写入任务分成小批次处理,每次只处理25个文件。使用setTimeout确保每次写入操作之间有短暂的间隔,从而避免瞬间打开过多文件句柄。

总结

EMFILE错误通常由短时间内打开过多文件句柄引起。通过调整系统限制或优化文件写入逻辑,可以有效解决这一问题。


几十个的话,可以用一个队列,一个一个写。 速度不会受什么影响

是的,这么几个文件,内容也不多,改成同步方法也是没什么影响,只是遇到问题了,想问下这是什么原因造成的。

linux这一限制是针对某个用户而不是进程 可能你别的地方打开的文件太多了,导致只剩248个

你可以运行 ulimit -n ${num_of_files_greater_than_1024} 来提高这一限制

你在使用 Node.js 的 fs.writeFile 方法时遇到 EMFILE 错误,这通常是因为文件描述符(file descriptors)的数量超出了系统的限制。尽管你在循环中只处理了几十个文件,但仍然可能遇到了文件描述符限制。

原因分析

  1. 文件描述符限制:操作系统为每个进程限制了可以同时打开的文件描述符数量。对于 macOS 和 Linux 系统,默认的限制通常是 256 或 1024。即使你的实际文件数量较少,这些文件描述符也可能被其他操作(如读取、网络请求等)占用。

  2. Node.js 版本:你提到的是 Node.js 0.8.x 版本,这个版本较旧,可能有一些已知的 bug 或限制。建议升级到最新稳定版本,以获得更好的性能和稳定性。

解决方案

  1. 增加文件描述符限制

    • 在 macOS 上,可以通过运行以下命令来临时提高文件描述符限制:
      ulimit -n 2048
      
    • 如果需要永久更改,可以编辑系统配置文件(如 /etc/sysctl.conf)或用户配置文件(如 ~/.bash_profile)。
  2. 优化文件写入逻辑

    • 使用批量写入或引入适当的延迟来避免同时打开过多文件。例如:
      const fs = require('fs');
      const path = require('path');
      
      function writeFileBatch(files, callback) {
        let completed = 0;
        files.forEach((fileContent, index) => {
          fs.writeFile(path.join(__dirname, fileContent.filename), fileContent.content, err => {
            if (err) return callback(err);
            completed++;
            if (completed === files.length) {
              callback(null);
            }
          });
        });
      }
      
      const files = Array.from({ length: 50 }, (_, i) => ({
        filename: `file${i}.txt`,
        content: 'some content'
      }));
      
      writeFileBatch(files, err => {
        if (err) throw err;
        console.log('All files written successfully.');
      });
      

通过这种方式,你可以减少同时打开的文件数量,并且更好地管理文件描述符的使用。

回到顶部