在 Node.js 中保存文件时,如果文件已经存在,如何递增文件名?

3
有没有一种简单的方法可以让node.js在保存文件时增加文件名(即追加一个数字),以避免覆盖之前的文件?
以下是我的尝试:
// can i make this a one-liner?:)
async function incrementIfExists(dirPath, fileName, data, increment=1) {
    const fs = require('fs'),
        path = require('path'),
        errorFunc = (err) => console.error(err);
        
    // Get the last file saved with same fileName (i.e., the one that has the greatest increment number), if there is one   
    let lastFile = await fs.promises.readdir(dirPath) 
        .then(files => {
            let result = '';
            
            for (const name of files) {
                if (!name.startsWith(fileName)) continue;
                if ((name.length < result.length) || (name.length === result.length && name < result)) continue;
                result = name;
            }
            
            return result;
        })
        
        .catch(errorFunc);
        
    if (lastFile) {
        const lastIncrementNr = Number(lastFile.slice((fileName + '_').length));
        if (increment <= lastIncrementNr) increment = lastIncrementNr + 1;
    }
    
    fileName = path.join(dirPath, fileName);
    
    while (true) {
        let breakLoop = await fs.promises.writeFile(lastFile ? fileName + '_' + increment : fileName, data, {encoding: 'utf8', flag: 'wx'})
            
            .then(fd => true)
            
            .catch(err => {
                if (err.code === 'EEXIST') {console.log(err);
                    return false;
                }
                throw err;
            });
        
        if (breakLoop) break;
        increment++;
    }
}

incrementIfExists('.', fileName, data);

相关链接:
如何在Node.js中避免覆盖文件
仅在Node.js中文件不存在时创建文件

1个回答

4
我使用类似于将图像文件上传到磁盘的版本。我决定使用“EEXIST”错误来递增编号,而不是在目录中显式迭代文件。

const writeFile = async(filename, data, increment = 0) => {
  const name = `${path.basename(filename, path.extname(filename))}${increment || ""}${path.extname(filename)}`
  return await fs.writeFile(name, data, { encoding: 'utf8', flag: 'wx' }).catch(async ex => {
    if (ex.code === "EEXIST") return await writeFile(filename, data, increment += 1)
    throw ex
  }) || name
}
const unversionedFile = await writeFile("./file.txt", "hello world")
const version1File = await writeFile("./file.txt", "hello world")
const version2File = await writeFile("./file.txt", "hello world")


好主意!我也应该先检查文件扩展名。起初,我认为我应该获取最后一个文件名,以避免在有数千个具有相同基本名称的文件时进行异步调用。但现在我看到了你的代码,我意识到这是不可行的,而且我在搜索文件夹中的所有文件时实际上浪费了时间,而你的代码更短。感谢分享! - flen
如果你在我手下工作,我会审查你的代码并拒绝它。通过移除异步操作,你已经删除了所有向调用者表明该方法是异步的标志。简洁的代码是好的,但缺乏意义的代码是不好的。 - BlueWater86
1
抱歉,我还在学习中,但它异步的意义是什么?它将返回一个Promise,但我的代码已经返回了一个Promise。如果你不再使用await,那么拥有一个async函数有什么好处呢? - flen
在这种情况下,我实际上没有足够好的理由来解释我所做的事情。对于之前评论中表现出来的语气,我感到抱歉。 - BlueWater86
2
没问题!我还是看不出有什么区别,但如果命名是个问题,你也可以把它命名为 const writeFileAsync。但你比我更了解你的代码,所以我就不多说了。现在我想我明白你的意思了,始终使用 async 可以让它更容易被发现,特别是对于自动化工作来说。 - flen
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接