¿Cómo se copia un archivo en PHP sin sobreescribir un archivo existente?

Cuando utiliza la función de copia de PHP, la operación copia ciegamente sobre el archivo de destino, incluso si ya existe. ¿Cómo se puede copiar un archivo de forma segura, solo se realiza la copia si no hay un archivo existente?

La solución obvia sería llamar a file_exists para verificar si el archivo existe, pero hacerlo podría causar una condición de carrera. Siempre existe la posibilidad de que el otro archivo se creará en el medio cuando llame a file_exists y cuando llame a copy . La única forma segura de verificar si el archivo existe es usar fopen .

Cuando llame a fopen , configure el modo a ‘x’. Esto le dice a fopen que cree el archivo, pero solo si no existe. Si existe, fopen fallará, y sabrá que no pudo crear el archivo. Si tiene éxito, tendrá creado un archivo en el destino que puede copiar de forma segura. El código de muestra está a continuación:

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

Creo que respondió su propia pregunta: verifique que el archivo de destino exista antes de realizar la copia. Si el archivo existe, omita la copia.

Actualización: veo que realmente respondiste tu propia pregunta. Menciona condiciones de carrera, pero si encuentra que el archivo ya existe, ¿cómo sabe que:

  • el archivo que ya está allí es realmente el que desea copiar
  • el otro proceso que copia el archivo ha completado su trabajo (los datos del archivo están todos allí)
  • el otro proceso que copia el archivo no va a fallar (y deja un archivo incompleto, o elimina el nuevo archivo)

Creo que deberías considerar estas preguntas al diseñar una solución a tu problema.

Intenta usar la función link() lugar de 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; } } 

Desafortunadamente, esto no funcionará en Windows.

Una función de tejón de miel, que simplemente no se preocupa por las condiciones de carrera, pero funciona multiplataforma.

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