在使用Node.js中的JavaScript删除Windows文件时出现问题

5
我有以下代码,试图删除Windows 7机器上的文件:
    // check if this item has an uploaded image file
    var imageFullPathName = __dirname + "/../public/images/" + req.params.itemId;
    logger.log("imageFullPathName = " + imageFullPathName);
    var normalizedPathName = path.normalize(imageFullPathName);
    logger.log("normalizedPathName = " + normalizedPathName);

    // delete the image if it exists
    fs.exists(normalizedPathName, function(exists) {
        console.log("Found the file: " + normalizedPathName);
        normalizedPathName = normalizedPathName.replace(/\\/g,"\\\\");
        console.log("New path name = " + normalizedPathName);
        fs.unlink(normalizedPathName, function(err){
            if (err){
                console.error("Error in call to fs.unlink");
            }
        });
    });

我得到了以下输出:

imageFullPathName = C:\IronKey\hes\cscie-71\project\medical-interchange\routes/../public/images/5658e5612103cb2c41000006

normalizedPathName = C:\IronKey\hes\cscie-71\project\medical-interchange\public\images\5658e5612103cb2c41000006

新路径名 = C:\\IronKey\\hes\\cscie-71\\project\\medical-interchange\\public\\images\\5658e5612103cb2c41000006

在调用fs.unlink时出错

当我使用DOS shell查找文件时,该文件确实存在。无论我如何更改新路径名,都无法删除该文件。如果我通过注释掉对normalizedPathName.replace()的调用来保留单个反斜杠的路径,它仍然失败。但是,如果我手动创建这样的字符串,它就能够工作:

fs.unlink("c:\\IronKey\\hes\\cscie-71\\project\\medical-interchange\\public\\images\\5658e5612103cb2c41000006", function(err){ ...

我该怎么做才能删除这个文件?我完全被难住了。

我按照josh3736的建议修改了代码,但我收到了一个ENOENT错误。由于错误消息中报告的完整路径名,该文件显然存在并且我已经在我的编辑器中打开了它:

        // check if this item has an uploaded image file
        var imageFullPathName = path.join(__dirname, "../public/images",
                                          sanitize(req.params.itemId));
        logger.log("imageFullPathName = " + imageFullPathName);

        fs.unlink(imageFullPathName, function(err){
            //if (err.code != 'ENOENT'){
            if (err){
                logger.log("Error in call to fs.unlink", err);
            }
            else{
                logger.log("No file found");
            }
            logger.log("Delete Success");
        });

错误信息如下: 调用fs.unlink时出错 {[Error: ENOENT:没有此文件或目录,unlink 'C:\\IronKey\\hes\\cscie-71\\project\\medical-interchange\\public\\images\\5658fd27fca1b3bc3d00000e']}


为什么你在使用一个版本的normalizedPathName时要执行fs.exists(),然后再尝试更改它才删除?此外,fs.exists()是一种反模式。只需尝试删除并处理不存在的错误即可。 - jfriend00
我对JavaScript和Node非常陌生。您能否解释一下“反模式”是什么意思? - Willard
现在来回答你的问题:fs.exists() 调用使用传递给它的路径。fs.unlink() 调用失败,我正在尝试找到一种使其工作的方法。没有其他原因。 - Willard
反模式意味着“做事情的错误方式”。您需要停止更改路径,只需在所需路径上调用fs.unlink()而不进行fs.exists()调用。如果不起作用,则查看返回的确切错误并使用该错误进行诊断。fs.exists()已弃用,因为存在并发问题。在多任务操作系统中,文件可以在调用fs.exists()和调用fs.unlink()之间被其他进程删除。您可以只调用fs.unlink()并处理错误(如果文件不存在)。没有理由进行fs.exists()调用。 - jfriend00
谢谢解释! - Willard
2个回答

2

这里存在几个问题。

  1. 你正在盲目地信任用户输入。攻击者只需要请求一个URL,将itemId设置为类似于%2E%2E%2F%2E%2E%2F%2E%2E%2FWindows的内容。Express会在req.params中对其进行反转义,变成../../../Windows,这样你的服务器就会出现问题。

    一般来说,处理不可信输入时要非常小心(而且所有通过HTTP传输的内容都是不可信的)。在这种情况下,使用像sanitize-filename这样的工具来确保输入中没有任何恶意内容。

  2. fs.exists是一种反模式。其他进程可能会在您调用exists和接下来的操作之间添加或删除相关文件。相反,无条件调用unlink——如果您得到一个err.code == 'ENOENT'的错误,则表示该文件不存在;只需忽略该错误即可。

  3. 为什么要使用normalizedPathName = normalizedPathName.replace(/\\/g,"\\\\");?你把一个有效的路径变成了无效的路径(C:\foo\barC:\\foo\\bar),所以当然会出问题。

  4. 在组装路径时,请使用path.join而不是字符串拼接。它会处理斜杠(\ vs /),使您的代码具有可移植性。

综上所述,

var imageFullPathName = path.join(
                                    __dirname,
                                    "../public/images",
                                    sanitizeFilename(req.params.itemId)
                                );

fs.unlink(imageFullPathName, function(err){
    if (err) {
        if (err.code != 'ENOENT') {
            // handle actual errors here
            console.error("Error in call to fs.unlink", err);
        }
        // else there was no file, handle that if you need to
    }
    // else delete success, handle that if you need to
});

既然您提到即使注释掉反斜杠replace行,您的代码仍然失败:

那么您的代码应该可以这样工作。实际错误是什么?我的第一个猜测是权限问题。


该文件肯定存在,因为我使用上述错误消息中报告的确切路径名在我的编辑器中打开了该文件。 - Willard
我在上面的原始问题中包含了修改后的代码和完整的错误信息,因为我在评论中没有足够的字符来完成它。 - Willard
这是错误信息: 调用fs.unlink时出错 {[Error: ENOENT:没有此文件或目录,unlink 'C:\ IronKey \ hes \ cscie-71 \ project \ medical-interchange \ public \ images \ 5658fd27fca1b \ \ u200b \ \ 3bc3d00000e']} - Willard
另外一个评论,我稍微修改了代码,以便通过删除“!= ENOENT”来确定错误。 - Willard
我的环境出现了一些奇怪的问题。在修复测试代码中的一些其他问题之后,图像删除代码开始删除文件,但我仍然收到ENOENT错误。很奇怪,我现在收到ENOENT错误,但是文件已经成功删除了。 - Willard
显示剩余2条评论

0

您可以使用Windows命令行永久删除文件,而不是使用fs.unlink

exec(`del ${imageFullPathName}`, (error, stdout, stderr) => {
  if (error) {
    console.error(`exec error: ${error}`);
    return;
  }
});

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