【问题标题】:How do you copy a file in PHP without overwriting an existing file?如何在 PHP 中复制文件而不覆盖现有文件?
【发布时间】:2008-10-22 18:00:53
【问题描述】:

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

【问题讨论】:

    标签: php file file-io


    【解决方案1】:

    显而易见的解决方案是调用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;
    }
    

    【讨论】:

    • 你仍然有竞争条件。但是您走在正确的轨道上——如果 fopen 成功,请使用 fwrite() 执行复制,并在完成后取消链接源文件。
    • 除了Nathan说的以外,用排他锁打开,防止复制过程中其他程序写入。
    【解决方案2】:

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

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

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

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

        【解决方案4】:

        尝试使用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 上运行。

        【讨论】:

          猜你喜欢
          • 2015-01-20
          • 2011-05-12
          • 1970-01-01
          • 2021-04-01
          • 2019-10-10
          • 2021-11-27
          • 2013-03-08
          • 1970-01-01
          • 2016-01-31
          相关资源
          最近更新 更多