Copie todo el contenido de un directorio a otro usando php

146

Traté de copiar todo el contenido del directorio a otra ubicación usando

copy ("old_location/*.*","new_location/");

pero dice que no puede encontrar flujo, verdadero *.*no se encuentra.

Cualquier otra forma

Gracias Dave

Dave
fuente
1
@ los editores: ¿Estás seguro de que "old_location/."solo fue un error tipográfico?
Felix Kling
Rich Rodecker tiene un script en su blog que parece hacer exactamente eso. visible-form.com/blog/copy-directory-in-php
Jon F Hancock el
@Felix: Me preguntaba lo mismo. Regresé a la primera revisión, pero lo ha hecho "old_location/*.*. No puedo encontrar una revisión que contenga "old_location/.".
Asaph
@Asaph: Tu retroceso estuvo bien, mira la historia ... quise decircopy ("old_location/.","new_location/");
Felix Kling
3
@dave ¿Cuándo recogerás un :) aceptado?
Nam G VU

Respuestas:

239

Parece que la copia solo maneja archivos únicos . Aquí hay una función para copiar de forma recursiva que encontré en esta nota en la página de documentación de copia :

<?php 
function recurse_copy($src,$dst) { 
    $dir = opendir($src); 
    @mkdir($dst); 
    while(false !== ( $file = readdir($dir)) ) { 
        if (( $file != '.' ) && ( $file != '..' )) { 
            if ( is_dir($src . '/' . $file) ) { 
                recurse_copy($src . '/' . $file,$dst . '/' . $file); 
            } 
            else { 
                copy($src . '/' . $file,$dst . '/' . $file); 
            } 
        } 
    } 
    closedir($dir); 
} 
?>
Felix Kling
fuente
2
Es un asterisco y no una estrella;)
Gordon
66
Funciona como un encanto .. Gracias @FelixKling
Milap
2
¿Por qué en @mkdirlugar de mkdir?
Oliboy50
3
@ Oliboy50: Puede preguntarle a la persona que escribió el código hace 5 años: php.net/manual/en/function.copy.php#91010 . Tal vez era más popular en aquel entonces suprimir los mensajes de error.
Felix Kling
1
@ Oliboy50: Ya veo. Creo que suprime cualquier mensaje de error. Aunque realmente nunca lo usé. Esta es la documentación: us3.php.net/manual/en/language.operators.errorcontrol.php
Felix Kling
90

Como se describe aquí , este es otro enfoque que también se ocupa de los enlaces simbólicos:

/**
 * Copy a file, or recursively copy a folder and its contents
 * @author      Aidan Lister <[email protected]>
 * @version     1.0.1
 * @link        http://aidanlister.com/2004/04/recursively-copying-directories-in-php/
 * @param       string   $source    Source path
 * @param       string   $dest      Destination path
 * @param       int      $permissions New folder creation permissions
 * @return      bool     Returns true on success, false on failure
 */
function xcopy($source, $dest, $permissions = 0755)
{
    $sourceHash = hashDirectory($source);
    // Check for symlinks
    if (is_link($source)) {
        return symlink(readlink($source), $dest);
    }

    // Simple copy for a file
    if (is_file($source)) {
        return copy($source, $dest);
    }

    // Make destination directory
    if (!is_dir($dest)) {
        mkdir($dest, $permissions);
    }

    // Loop through the folder
    $dir = dir($source);
    while (false !== $entry = $dir->read()) {
        // Skip pointers
        if ($entry == '.' || $entry == '..') {
            continue;
        }

        // Deep copy directories
        if($sourceHash != hashDirectory($source."/".$entry)){
             xcopy("$source/$entry", "$dest/$entry", $permissions);
        }
    }

    // Clean up
    $dir->close();
    return true;
}

// In case of coping a directory inside itself, there is a need to hash check the directory otherwise and infinite loop of coping is generated

function hashDirectory($directory){
    if (! is_dir($directory)){ return false; }

    $files = array();
    $dir = dir($directory);

    while (false !== ($file = $dir->read())){
        if ($file != '.' and $file != '..') {
            if (is_dir($directory . '/' . $file)) { $files[] = hashDirectory($directory . '/' . $file); }
            else { $files[] = md5_file($directory . '/' . $file); }
        }
    }

    $dir->close();

    return md5(implode('', $files));
}
itsjavi
fuente
Funcionó muy bien para copiar una carpeta con 140 subcarpetas y cada subcarpeta que contiene 21 imágenes. ¡Funciona genial! ¡Gracias!
Darksaint2014
1
mkdirdebe agregarse truecomo el último parámetro para admitir el directorio recursivo, entonces este script es perfecto
ZenithS
Esto copia la carpeta completa? ¿Qué sucede si solo desea copiar los archivos dentro de la carpeta, sin la carpeta principal, como dice la pregunta: copy ("old_location/*.*","new_location/");¿Funciona eso? ¿Qué pasa si tiene archivos de puntos, serán emparejados?
XCS
35

copy () solo funciona con archivos.

Tanto los comandos de copia de DOS como los de Unix cp se copiarán de forma recursiva, por lo que la solución más rápida es simplemente utilizarlos. p.ej

`cp -r $src $dest`;

De lo contrario, deberá usar el opendir/ readdiro scandirpara leer el contenido del directorio, iterar a través de los resultados y si is_dir devuelve verdadero para cada uno, repita en él.

p.ej

function xcopy($src, $dest) {
    foreach (scandir($src) as $file) {
        if (!is_readable($src . '/' . $file)) continue;
        if (is_dir($src .'/' . $file) && ($file != '.') && ($file != '..') ) {
            mkdir($dest . '/' . $file);
            xcopy($src . '/' . $file, $dest . '/' . $file);
        } else {
            copy($src . '/' . $file, $dest . '/' . $file);
        }
    }
}
symcbean
fuente
1
Aquí hay una versión más estable y limpia de xcopy () que no crea la carpeta si existe: function xcopy($src, $dest) { foreach (scandir($src) as $file) { $srcfile = rtrim($src, '/') .'/'. $file; $destfile = rtrim($dest, '/') .'/'. $file; if (!is_readable($srcfile)) { continue; } if ($file != '.' && $file != '..') { if (is_dir($srcfile)) { if (!file_exists($destfile)) { mkdir($destfile); } xcopy($srcfile, $destfile); } else { copy($srcfile, $destfile); } } } }
TheStoryCoder
¡Gracias por la solución de backtick ! Una página que me ayudó a ajustar el comando de copia: UNIX cp explicó . Información adicional: PHP> = 5.3 ofrece algunos buenos iteradores
maxpower9000
21

¡La mejor solución es!

<?php
$src = "/home/www/domain-name.com/source/folders/123456";
$dest = "/home/www/domain-name.com/test/123456";

shell_exec("cp -r $src $dest");

echo "<H3>Copy Paste completed!</H3>"; //output when done
?>
negro
fuente
31
No funcionará en servidores Windows u otros entornos en los que o bien tienen ningún acceso a cualquiera shell_execo cp. Eso hace que, en mi opinión, no sea la "mejor" solución.
The Pellmeister
3
Aparte de eso, los controles de línea de comandos de un archivo PHP pueden ser un gran problema cuando alguien encuentra la manera de obtener un archivo en su servidor.
Martijn
¡Trabajado como un encanto! En CentOS y funcionó muy bien. Gracias @bstpierre
Nick Green
1
Esto no funcionará en Windows en absoluto, porque cpes un comando de Linux. Para uso de Windows xcopy dir1 dir2 /e /i, donde /esignifica copiar /idirectorios vacíos e ignorar preguntas sobre archivos o directorios
Michel
Sí, es la mejor solución si su servidor admite este comando y tiene los permisos necesarios. Es muy rápido. Lamentablemente no funciona en todos los entornos.
mdikici
13
function full_copy( $source, $target ) {
    if ( is_dir( $source ) ) {
        @mkdir( $target );
        $d = dir( $source );
        while ( FALSE !== ( $entry = $d->read() ) ) {
            if ( $entry == '.' || $entry == '..' ) {
                continue;
            }
            $Entry = $source . '/' . $entry; 
            if ( is_dir( $Entry ) ) {
                full_copy( $Entry, $target . '/' . $entry );
                continue;
            }
            copy( $Entry, $target . '/' . $entry );
        }

        $d->close();
    }else {
        copy( $source, $target );
    }
}
kzoty
fuente
¡Funciona perfectamente! Gracias hermano
Robin Delaporte
8

Como se dijo en otra parte, copysolo funciona con un solo archivo para la fuente y no con un patrón. Si desea copiar por patrón, use globpara determinar los archivos, luego ejecute la copia. Sin embargo, esto no copiará subdirectorios ni creará el directorio de destino.

function copyToDir($pattern, $dir)
{
    foreach (glob($pattern) as $file) {
        if(!is_dir($file) && is_readable($file)) {
            $dest = realpath($dir . DIRECTORY_SEPARATOR) . basename($file);
            copy($file, $dest);
        }
    }    
}
copyToDir('./test/foo/*.txt', './test/bar'); // copies all txt files
Gordon
fuente
considere cambiar: $ dest = realpath ($ dir. DIRECTORY_SEPARATOR). nombre base ($ archivo); con: $ dest = realpath ($ dir). DIRECTORIO_SEPARADOR. nombre base ($ archivo);
Dawez
8
<?php
    function copy_directory( $source, $destination ) {
        if ( is_dir( $source ) ) {
        @mkdir( $destination );
        $directory = dir( $source );
        while ( FALSE !== ( $readdirectory = $directory->read() ) ) {
            if ( $readdirectory == '.' || $readdirectory == '..' ) {
                continue;
            }
            $PathDir = $source . '/' . $readdirectory; 
            if ( is_dir( $PathDir ) ) {
                copy_directory( $PathDir, $destination . '/' . $readdirectory );
                continue;
            }
            copy( $PathDir, $destination . '/' . $readdirectory );
        }

        $directory->close();
        }else {
        copy( $source, $destination );
        }
    }
?>

desde la última cuarta línea, haz esto

$source = 'wordpress';//i.e. your source path

y

$destination ='b';
Hemanta Nandi
fuente
7

Agradezco totalmente a Felix Kling por su excelente respuesta que agradecidamente he usado en mi código. Ofrezco una pequeña mejora de un valor de retorno booleano para informar el éxito o el fracaso:

function recurse_copy($src, $dst) {

  $dir = opendir($src);
  $result = ($dir === false ? false : true);

  if ($result !== false) {
    $result = @mkdir($dst);

    if ($result === true) {
      while(false !== ( $file = readdir($dir)) ) { 
        if (( $file != '.' ) && ( $file != '..' ) && $result) { 
          if ( is_dir($src . '/' . $file) ) { 
            $result = recurse_copy($src . '/' . $file,$dst . '/' . $file); 
          }     else { 
            $result = copy($src . '/' . $file,$dst . '/' . $file); 
          } 
        } 
      } 
      closedir($dir);
    }
  }

  return $result;
}
gonzo
fuente
1
está utilizando recurse_copy () y recurseCopy () como nombres de funciones, actualícelo.
AgelessEssence
El closedir ($ dir); La instrucción debe estar fuera de la instrucción if ($ reslut === true), es decir, una llave más abajo. De lo contrario, corre el riesgo de tener recursos no liberados.
Dimitar Darazhanski el
5

Mi versión podada de @Kzoty responde. Gracias Kzoty

Uso

Helper::copy($sourcePath, $targetPath);

class Helper {

    static function copy($source, $target) {
        if (!is_dir($source)) {//it is a file, do a normal copy
            copy($source, $target);
            return;
        }

        //it is a folder, copy its files & sub-folders
        @mkdir($target);
        $d = dir($source);
        $navFolders = array('.', '..');
        while (false !== ($fileEntry=$d->read() )) {//copy one by one
            //skip if it is navigation folder . or ..
            if (in_array($fileEntry, $navFolders) ) {
                continue;
            }

            //do copy
            $s = "$source/$fileEntry";
            $t = "$target/$fileEntry";
            self::copy($s, $t);
        }
        $d->close();
    }

}
Nam G VU
fuente
1

Clono todo el directorio mediante el iterador de directorio SPL.

function recursiveCopy($source, $destination)
{
    if (!file_exists($destination)) {
        mkdir($destination);
    }

    $splFileInfoArr = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);

    foreach ($splFileInfoArr as $fullPath => $splFileinfo) {
        //skip . ..
        if (in_array($splFileinfo->getBasename(), [".", ".."])) {
            continue;
        }
        //get relative path of source file or folder
        $path = str_replace($source, "", $splFileinfo->getPathname());

        if ($splFileinfo->isDir()) {
            mkdir($destination . "/" . $path);
        } else {
        copy($fullPath, $destination . "/" . $path);
        }
    }
}
#calling the function
recursiveCopy(__DIR__ . "/source", __DIR__ . "/destination");
Tuhin Bepari
fuente
0
// using exec

function rCopy($directory, $destination)
{

    $command = sprintf('cp -r %s/* %s', $directory, $destination);

    exec($command);

}
Łukasz Szpak
fuente
0

Para los servidores Linux solo necesita una línea de código para copiar de forma recursiva mientras conserva el permiso:

exec('cp -a '.$source.' '.$dest);

Otra forma de hacerlo es:

mkdir($dest);
foreach ($iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS), \RecursiveIteratorIterator::SELF_FIRST) as $item)
{
    if ($item->isDir())
        mkdir($dest.DIRECTORY_SEPARATOR.$iterator->getSubPathName());
    else
        copy($item, $dest.DIRECTORY_SEPARATOR.$iterator->getSubPathName());
}

pero es más lento y no conserva los permisos.

Dan Bray
fuente
0

Tuve una situación similar en la que necesitaba copiar de un dominio a otro en el mismo servidor. Esto es exactamente lo que funcionó en mi caso, también puede ajustarlo para adaptarse al suyo:

foreach(glob('../folder/*.php') as $file) {
$adjust = substr($file,3);
copy($file, '/home/user/abcde.com/'.$adjust);

Observe el uso de "substr ()", sin él, el destino se convierte en '/home/user/abcde.com/../folder/', que podría ser algo que no desea. Entonces, utilicé substr () para eliminar los primeros 3 caracteres (../) para obtener el destino deseado que es '/home/user/abcde.com/folder/'. Por lo tanto, puede ajustar la función substr () y también la función glob () hasta que se ajuste a sus necesidades personales. Espero que esto ayude.

Chimdi
fuente