7

当您使用 PHP复制功能时,该操作会盲目地复制目标文件,即使它已经存在。您如何安全地复制文件,仅在没有现有文件的情况下执行复制?

4

4 回答 4

8

显而易见的解决方案是调用file_exists来检查文件是否存在,但这样做可能会导致竞争条件。当您调用file_exists和调用copy时,总是有可能在两者之间创建另一个文件。检查文件是否存在的唯一安全方法是使用fopen

当您调用fopen时,将模式设置为“x”。这告诉fopen创建文件,但前提是它不存在。如果它存在,fopen将失败,并且您将知道您无法创建该文件。如果成功,您将在目的地创建一个可以安全复制的文件。示例代码如下:

// The PHP copy function blindly copies over existing files.  We don't wish
// this to happen, so we have to perform the copy a bit differently.  The
// only safe way to ensure we don't overwrite an existing file is to call
// fopen in create-only mode (mode 'x').  If it succeeds, the file did not
// exist before, and we've successfully created it, meaning we own the
// file.  After that, we can safely copy over our own file.

$filename = 'sourcefile.txt'
$copyname = 'sourcefile_copy.txt'
if ($file = @fopen($copyname, 'x')) {
    // We've successfully created a file, so it's ours.  We'll close
    // our handle.
    if (!@fclose($file)) {
        // There was some problem with our file handle.
        return false;
    }

    // Now we copy over the file we created.
    if (!@copy($filename, $copyname)) {
        // The copy failed, even though we own the file, so we'll clean
        // up by itrying to remove the file and report failure.
        unlink($copyname);
        return false;
    }

    return true;
}
于 2008-10-22T18:03:09.897 回答
3

我想你回答了你自己的问题 - 在执行复制之前检查以确保目标文件存在。如果文件存在,则跳过副本。

更新:我看到你确实回答了你自己的问题。您提到了竞争条件,但是如果您确实发现该文件已经存在,您怎么知道:

  • 已经存在的文件确实是您要复制的文件
  • 复制文件的另一个进程已经完成了它的工作(文件数据都在那里)
  • 复制文件的其他进程不会失败(并留下不完整的文件,或删除新文件)

我认为在设计问题的解决方案时应该考虑这些问题。

于 2008-10-22T18:01:54.533 回答
1

一个蜜獾函数,它不关心竞争条件,但可以跨平台工作。

function safeCopy($src, $dest) {
    if (is_file($dest) === true) {
        // if the destination file already exists, it will NOT be overwritten.        
        return false;
    }

    if (copy($src, $dest) === false) {
        echo "Failed to copy $src... Permissions correct?\n";
        return false;
    }

    return true;   
}
于 2014-03-26T06:38:17.163 回答
0

尝试使用该link()函数而不是copy().

function safe_copy($src, $dest) {
    if (link($src, $dest)) {
        // Link succeeded, remove old name
        unlink($filename);
        return true;
    } else {
        // Link failed; filesystem has not been altered
        return false;
    }
}

不幸的是,这不适用于 Windows。

于 2009-02-13T21:14:51.060 回答