【问题标题】:Nodejs write multiple dynamically changing files with fs writefileNodejs用fs writefile写入多个动态变化的文件
【发布时间】:2022-02-14 03:11:52
【问题描述】:

我需要基于一个数组编写多个动态变化的文件,该数组由传递给自定义 writeData() 函数的对象组成。该数组由包含文件名和要写入的数据的对象组成,如下所示:

[
  {
    file_name: "example.json",
    dataObj,
  },
  {
    file_name: "example2.json",
    dataObj,
  },
  {
    file_name: "example3.json",
    dataObj,
  },
  {
    file_name: "example4.json",
    dataObj,
  },
];

我目前的方法是映射这个数组并读取+写入新数据到每个文件:

array.map((entry) => {
  fs.readFile(
    entry.file_name,
    "utf8",
    (err, unparsedData) => {
      if (err) console.log(err);
      else {
        var parsedData = JSON.parse(unparsedData);
        parsedData.data.push(entry.dataObj);
        const parsedDataJSON = JSON.stringify(parsedData, null, 2);
        fs.writeFile(
          entry.file_name,
          parsedDataJSON,
          "utf8",
          (err) => {
            if (err) console.log(err);
          }
        );
      }
    }
  );
});

但是,这不起作用。只有一小部分数据被写入这些文件,而且文件通常没有以 json 格式正确写入(我认为这是因为两个 writeFile 进程同时写入同一个文件,这会破坏文件)。显然这并不像我预期的那样工作。

我尝试解决此问题的多种方法包括尝试使 fs.writeFile 同步(延迟映射循环,允许每个进程在移动到下一个条目之前完成),但这不是一个好习惯,因为同步进程挂起整个应用程序。我也研究过实现承诺,但无济于事。我是 nodejs 的新手,因此对于错过的详细信息/信息深表歉意。任何帮助表示赞赏!

【问题讨论】:

  • 您是否多次调用此代码并且没有等待它完成再调用它。就其本身而言,它不会引起问题,因为您的循环每次循环迭代都会写入不同的文件。但是,如果你不止一次调用它,那将是一个问题,我们需要查看调用代码来帮助修复它。
  • fs.readFile()fs.writeFile() 返回一个未解决的承诺。所以基本上。我会将你所有的fs.writeFile() 函数放在一个数组中并使用 Promise.all() 执行。您可以等待每个请求,但是您基本上是将异步代码转换为同步代码。 fs.readFile:nodejs.org/api/fs.html#filehandlereadfileoptions,fs.writeFile:nodejs.org/api/fs.html#filehandlewritefiledata-options
  • 为什么要使用 Array.map?运行后你期望返回什么数组,写结果?
  • @Brettski - fs.readFile()fs.writeFile() 不返回承诺。那将是fs.promises.readFile()fs.promises.writeFile()
  • 我的错,是的。看起来不正确的是 entry.dataObj 将是最后一次迭代中的那个。也许将它包装在 IIFE(立即调用函数表达式)中以限定 entry 变量的范围。自从我从事回调工作以来已经有一段时间了。

标签: node.js loops fs


【解决方案1】:

如果更改任何内容,同一文件通常会在数组中多次列出。

嗯,这改变了一切。您应该在原始问题中证明这一点。如果是这种情况,那么您必须对循环中的每个单独文件进行排序,以便在前进到下一个之前完成一个文件。为防止写入同一个文件之间发生冲突,您必须确保两件事:

  1. 您对循环中的每个文件进行排序,以便在前一个文件完成之前下一个文件不会开始。
  2. 当它仍在运行时,您不要再次调用此代码。

您可以像这样向自己保证第一项:

async function processFiles(array) {
    for (let entry of array) {
        const unparsedData = await fs.promises.readFile(entry.file_name, "utf8");
        const parsedData = JSON.parse(unparsedData);
        parsedData.data.push(entry.dataObj);
        const json = JSON.stringify(parsedData, null, 2);
        await fs.promise.writeFile(entry.file_name, json, "utf8");
    }
}

如果其中任何一个出现错误,这将中止循环。如果你想让它继续写其他的,你可以在内部添加一个try/catch

async function processFiles(array) {
    let firstError;
    for (let entry of array) {
        try {
            const unparsedData = await fs.promises.readFile(entry.file_name, "utf8");
            const parsedData = JSON.parse(unparsedData);
            parsedData.data.push(entry.dataObj);
            const json = JSON.stringify(parsedData, null, 2);
            await fs.promise.writeFile(entry.file_name, json, "utf8");
        } catch (e) {
            // log error and continue with the rest of the loop
            if (!firstError) {
                firstError = e;
            }
            console.log(e);
        }
    }
    // make sure we communicate back any error that happened
    if (firstError) {
        throw firstError;
    }
}

为了确保自己了解上述第二点,您必须要么不使用setInterval()(将其替换为您在processFiles()resolves 的承诺时设置的setTimeout(),或者绝对确保@987654327 @时间足够长,在processFiles()完成之前它永远不会触发。


另外,请绝对确保您没有在该函数运行时修改该函数中使用的array

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-17
    • 2017-04-07
    • 2016-12-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-01-24
    • 1970-01-01
    相关资源
    最近更新 更多