如何在不覆盖现有文件的情况下在PHP中复制文件?

时间:2021-02-04 20:26:00

When you use the PHP copy function, the operation blindly copies over the destination file, even if it already exists. How do you copy a file safely, only performing the copy if there is no existing file?

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

4 个解决方案

#1


8  

The obvious solution would be to call file_exists to check to see if the file exists, but doing that could cause a race condition. There is always the possibility that the other file will be created in between when you call file_exists and when you call copy. The only safe way to check if the file exists is to use fopen.

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

When you call fopen, set the mode to 'x'. This tells fopen to create the file, but only if it doesn't exist. If it exists, fopen will fail, and you'll know that you couldn't create the file. If it succeeds, you will have a created a file at the destination that you can safely copy over. Sample code is below:

当您调用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;
}

#2


3  

I think you answered your own question - check to make sure the destination file exists before performing the copy. If the file exists, skip the copy.

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

Update: I see you really did answer your own question. You mention race conditions, but if you do find that the file already exists, how do you know that:

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

  • the file that is already there is really the one you want to copy
  • 已存在的文件实际上是您要复制的文件

  • the other process copying the file has completed its job (the file data is all there)
  • 复制文件的其他进程已完成其工作(文件数据全部存在)

  • the other process copying the file isn't going to fail (and leave an incomplete file, or delete the new file)
  • 复制文件的其他进程不会失败(并保留不完整的文件,或删除新文件)

I think you should consider these questions when designing a solution to your problem.

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

#3


0  

Try using the link() function instead of copy().

尝试使用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;
    }
}

Unfortunately, this will not work on Windows.

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

#4


0  

A honey badger function, which just doesn't care about race-conditions, but works cross-platform.

蜂蜜獾功能,它不关心竞争条件,但跨平台工作。

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;   
}

#1


8  

The obvious solution would be to call file_exists to check to see if the file exists, but doing that could cause a race condition. There is always the possibility that the other file will be created in between when you call file_exists and when you call copy. The only safe way to check if the file exists is to use fopen.

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

When you call fopen, set the mode to 'x'. This tells fopen to create the file, but only if it doesn't exist. If it exists, fopen will fail, and you'll know that you couldn't create the file. If it succeeds, you will have a created a file at the destination that you can safely copy over. Sample code is below:

当您调用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;
}

#2


3  

I think you answered your own question - check to make sure the destination file exists before performing the copy. If the file exists, skip the copy.

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

Update: I see you really did answer your own question. You mention race conditions, but if you do find that the file already exists, how do you know that:

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

  • the file that is already there is really the one you want to copy
  • 已存在的文件实际上是您要复制的文件

  • the other process copying the file has completed its job (the file data is all there)
  • 复制文件的其他进程已完成其工作(文件数据全部存在)

  • the other process copying the file isn't going to fail (and leave an incomplete file, or delete the new file)
  • 复制文件的其他进程不会失败(并保留不完整的文件,或删除新文件)

I think you should consider these questions when designing a solution to your problem.

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

#3


0  

Try using the link() function instead of copy().

尝试使用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;
    }
}

Unfortunately, this will not work on Windows.

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

#4


0  

A honey badger function, which just doesn't care about race-conditions, but works cross-platform.

蜂蜜獾功能,它不关心竞争条件,但跨平台工作。

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;   
}