【问题标题】:Error running Node.js' fs.rename() inside of a loop在循环内运行 Node.js 的 fs.rename() 时出错
【发布时间】:2018-05-01 14:13:21
【问题描述】:

我正在开发一个必须组织一些上传文件的应用程序,我将它们分隔在具有请求 ID 的文件夹中(我正在使用 express-request-id 来获取此 ID)。

问题是每次我有多个文件时,“移动”过程都会失败,我似乎无法修复它。

let request_folder = path.resolve(tmp_folder + "/" + req.id);

/* Checking if the folder exists */
fs.access(request_folder, fs.constants.F_OK, function(error) {
    if(error) { // it doesn't
        /* Trying to create it */
        fs.mkdir(request_folder, function(error) {
            if(error) {
                console.log("Error: Couldn't create the directory.");
                console.log(error);
            }
        });
    }
});

/* Moving uploaded files to their respective request folder */
req.files.forEach(function(file) {
    let new_file_path = path.resolve(request_folder + "/" + file.filename);
    fs.rename(file.path, new_file_path, function(error) {
        if(error) {
            console.log("Error: Couldn't move " + file.filename + ".");
            console.log(error);
        }
    });
});

我百分百确定文件夹和文件都存在,但是当我尝试一次移动两个文件时,我得到:

Error: Couldn't move Desert.jpg.
{ Error: ENOENT: no such file or directory, rename 'C:\Users\telmo.silva\csc-links\public\tmp\Desert.jpg' -> 'C:\Users\telmo.silva\csc-links\public\tmp\d2abf375-d09f-440c-a5ba-adf4f5725a73
\Desert.jpg'
  errno: -4058,
  code: 'ENOENT',
  syscall: 'rename',
  path: 'C:\\Users\\telmo.silva\\csc-links\\public\\tmp\\Desert.jpg',
  dest: 'C:\\Users\\telmo.silva\\csc-links\\public\\tmp\\d2abf375-d09f-440c-a5ba-adf4f5725a73\\Desert.jpg' }
Error: Couldn't move Chrysanthemum.jpg.
{ Error: ENOENT: no such file or directory, rename 'C:\Users\telmo.silva\csc-links\public\tmp\Chrysanthemum.jpg' -> 'C:\Users\telmo.silva\csc-links\public\tmp\d2abf375-d09f-440c-a5ba-adf4f
5725a73\Chrysanthemum.jpg'
  errno: -4058,
  code: 'ENOENT',
  syscall: 'rename',
  path: 'C:\\Users\\telmo.silva\\csc-links\\public\\tmp\\Chrysanthemum.jpg',
  dest: 'C:\\Users\\telmo.silva\\csc-links\\public\\tmp\\d2abf375-d09f-440c-a5ba-adf4f5725a73\\Chrysanthemum.jpg' }

有谁知道我做错了什么?谢谢!

【问题讨论】:

    标签: javascript node.js express multer


    【解决方案1】:

    您使用的 fs 操作是异步的,这意味着它们可以按任何顺序发生。

    您的循环将要求节点创建一个文件夹并基本上同时移动文件。换句话说,它将运行一个同步循环,同时调度所有fs 动作。这意味着您无法保证实际首先运行的是什么。

    在移动所有文件之前尝试创建文件夹:

    fs.access(request_folder, fs.constants.F_OK, function(error) {
        if(error) {
            return fs.mkdir(request_folder, function(error) {
                if(error) {
                    return;
                }
                moveFiles();
            });
        }
        moveFiles();
    });
    
    function moveFiles() {
      req.files.forEach(function(file) {
        // ...
      });
    }
    

    使用 Promise 可能会更简洁:

    const access = util.promisify(fs.access);
    const mkdir = util.promisify(fs.mkdir);
    const rename = util.promisify(fs.rename);
    
    access(request_folder, fs.constants.F_OK)
      .then(moveFiles, makeDirAndMoveFiles)
      .catch(console.error);
    
    function moveFiles() {
      return Promise.all(
        req.files.map(file => {
          const new_file_path = path.resolve(request_folder + "/" + file.filename);
          return rename(file.path, new_file_path);
        })
      )
    }
    
    function makeDirAndMoveFiles() {
      return mkdir(request_folder).then(moveFiles);
    }
    

    【讨论】:

    • 你说得对,我没有意识到操作顺序可以颠倒,因为两个函数都是异步的。我选择了您的答案,因为它显示了一种通过回调和承诺来解决它的方法。
    【解决方案2】:

    您可能在目标文件夹存在之前就尝试移动文件。

    尝试使用新的 await 关键字来构建代码。

    const {promisify} = require('util');
    
    const fs = require('fs');
    const accessFileAsync = promisify(fs.access);
    const mkdirFileAsync = promisify(fs.mkdir);
    const renameFileAsync = promisify(fs.rename);
    try{
      await accessFileAsync(request_folder, fs.constants.F_OK);
    }
    catch(ex)
    {
      await mkdirFileAsync(request_folder);
    }
    req.files.forEach(function(file) {
        let new_file_path = path.resolve(request_folder + "/" + file.filename);
        try{
          await renameFileAsync(file.path, new_file_path);
        }
        catch(ex)
        {
          console.log(ex);
        }
    });
    

    【讨论】:

    • await 只能在async 函数中使用
    • 感谢您的回答,您确实是对的,我使用了两个异步函数而不保证优先顺序。不过,我最终接受了另一个答案,因为它显示了一种使用回调和承诺进行编码的方法。
    • 如上所述,您只能在异步函数中使用 await。
    猜你喜欢
    • 2012-10-19
    • 2019-01-19
    • 2015-05-30
    • 2013-07-21
    • 1970-01-01
    • 2016-02-14
    • 2011-12-19
    • 2020-07-02
    • 1970-01-01
    相关资源
    最近更新 更多