Copia l'intero contenuto di una directory in un'altra usando php


146

Ho provato a copiare l'intero contenuto della directory in un'altra posizione usando

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

ma dice che non riesce a trovare stream, true *.*non è stato trovato.

Qualsiasi altro modo

Grazie Dave


1
@gli editori: sei sicuro che "old_location/."fosse solo un errore di battitura?
Felix Kling,

Rich Rodecker ha una sceneggiatura sul suo blog che sembra fare proprio questo. visible-form.com/blog/copy-directory-in-php
Jon F Hancock

@Felix: mi chiedevo la stessa cosa. Sono tornato alla prima revisione, ma è così "old_location/*.*. Non riesco a trovare una revisione contenente "old_location/.".
Asaf,

@Asaph: Il tuo rollback era ok, guarda la storia ... Intendevocopy ("old_location/.","new_location/");
Felix Kling,

3
@dave Quando prenderai un accettato :)?
Nam G VU,

Risposte:


239

Sembra che la copia gestisca solo singoli file . Ecco una funzione per la copia ricorsiva che ho trovato in questa nota nella pagina della documentazione della 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); 
} 
?>

2
È un asterisco e non una stella;)
Gordon,

6
Funziona come un fascino .. Grazie @FelixKling
Milap

2
Perché @mkdirinvece di mkdir?
Oliboy50,

3
@ Oliboy50: puoi chiedere a chi ha scritto il codice 5 anni fa: php.net/manual/en/function.copy.php#91010 . Forse allora era più popolare sopprimere i messaggi di errore.
Felix Kling,

1
@ Oliboy50: vedo. Penso che elimini qualsiasi messaggio di errore. Non l'ho mai usato davvero. Questa è la documentazione: us3.php.net/manual/en/language.operators.errorcontrol.php
Felix Kling

90

Come descritto qui , questo è un altro approccio che si occupa anche dei collegamenti simbolici:

/**
 * Copy a file, or recursively copy a folder and its contents
 * @author      Aidan Lister <aidan@php.net>
 * @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));
}

Ha funzionato alla grande per copiare una cartella con 140 sottocartelle e ogni sottocartella contenente 21 immagini. Funziona alla grande! Grazie!
Darksaint2014,

1
mkdirdovrebbe essere aggiunto truecome ultimo parametro per supportare la directory ricorsiva, quindi questo script è perfetto
ZenithS

Questo copia l'intera cartella? Cosa succede se si desidera solo copiare i file all'interno della cartella, senza la cartella principale, come dice la domanda: copy ("old_location/*.*","new_location/");Funziona? Che cosa succede se si dispone di file dot, verranno abbinati?
XCS

35

copy () funziona solo con i file.

Sia la copia DOS che i comandi Unix cp verranno copiati in modo ricorsivo, quindi la soluzione più rapida è semplicemente sborsare e usarli. per esempio

`cp -r $src $dest`;

Altrimenti dovrai usare il opendir/ readdiro scandirper leggere il contenuto della directory, scorrere i risultati e se is_dir restituisce true per ognuno, ricorrere in esso.

per esempio

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

1
Ecco una versione più stabile e più pulita di xcopy () che non crea la cartella se esiste: 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

Grazie per la soluzione backtick ! Una pagina che mi ha aiutato a modificare il comando di copia: spiegazione cp UNIX . Ulteriori informazioni: PHP> = 5.3 offre alcuni bei iteratori
maxpower9000,

21

La migliore soluzione è!

<?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
?>

31
Non funziona su server Windows o altri ambienti in cui non hai accesso a shell_execo cp. Questo lo rende - secondo me - difficilmente la soluzione "migliore".
The Pellmeister,

3
A parte questo, i controlli da riga di comando da un file PHP possono essere un grosso problema quando qualcuno trova un modo per ottenere un file sul tuo server.
Martijn,

Ha funzionato come un fascino! Su CentOS e ha funzionato alla grande. Grazie @bstpierre
Nick Green,

1
Questo non funzionerà affatto su Windows, perché cpè un comando Linux. Per l'uso su Windows xcopy dir1 dir2 /e /i, dove /esta per copiare directory vuote e /iper ignorare le domande su file o directory
Michel,

Sì, è la soluzione migliore se il tuo server supporta questo comando e hai le autorizzazioni necessarie. È molto veloce. Purtroppo non funziona in tutti gli ambienti.
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 );
    }
}

Funziona perfettamente! Grazie fratello
Robin Delaporte il

8

Come detto altrove, copyfunziona solo con un singolo file per l'origine e non un modello. Se si desidera copiare in base al modello, utilizzare globper determinare i file, quindi eseguire copia. Questo tuttavia non copierà le sottodirectory, né creerà la directory di destinazione.

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

considerare di cambiare: $ dest = realpath ($ dir. DIRECTORY_SEPARATOR). basename ($ file); con: $ dest = realpath ($ dir). DIRECTORY_SEPARATOR. basename ($ file);
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 );
        }
    }
?>

dall'ultima quarta riga, crea questo

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

e

$destination ='b';

7

Un ringraziamento totale deve essere andato a Felix Kling per la sua eccellente risposta che ho usato con gratitudine nel mio codice. Offro un piccolo miglioramento di un valore di ritorno booleano per segnalare l'esito positivo o negativo:

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

1
stai usando recurse_copy () e recurseCopy () come nomi di funzioni, aggiornalo.
AgelessEssence,

The closedir ($ dir); L'istruzione deve essere al di fuori dell'istruzione if ($ reslut === true), ovvero una parentesi graffa più in basso. Altrimenti rischi di avere risorse non liberate.
Dimitar Darazhanski,


5

La mia versione potata della risposta @Kzoty. Grazie 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();
    }

}

1

Clono l'intera directory tramite SPL Directory Iterator.

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");

0
// using exec

function rCopy($directory, $destination)
{

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

    exec($command);

}

0

Per i server Linux è sufficiente copiare una riga di codice in modo ricorsivo conservando le autorizzazioni:

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

Un altro modo di farlo è:

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

ma è più lento e non conserva le autorizzazioni.


0

Ho avuto una situazione simile in cui dovevo copiare da un dominio all'altro sullo stesso server. Ecco esattamente cosa ha funzionato nel mio caso, puoi anche adattarti al tuo:

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

Notare l'uso di "substr ()", senza di esso, la destinazione diventa '/home/user/abcde.com/../folder/', che potrebbe essere qualcosa che non si desidera. Quindi, ho usato substr () per eliminare i primi 3 caratteri (../) per ottenere la destinazione desiderata che è '/home/user/abcde.com/folder/'. Quindi, puoi regolare la funzione substr () e anche la funzione glob () fino a quando non si adatta alle tue esigenze personali. Spero che questo ti aiuti.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.