Copiez tout le contenu d'un répertoire dans un autre en utilisant php

146

J'ai essayé de copier tout le contenu du répertoire vers un autre emplacement en utilisant

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

mais il dit qu'il ne peut pas trouver de flux, true *.*n'est pas trouvé.

Tout autre moyen

Merci Dave

Dave
la source
1
@les éditeurs: Etes-vous sûr que ce "old_location/."n'était qu'une faute de frappe?
Felix Kling
Rich Rodecker a un script sur son blog qui semble faire exactement cela. visible-form.com/blog/copy-directory-in-php
Jon F Hancock
@Felix: Je me demandais la même chose. Je suis revenu à la première révision mais c'est le cas "old_location/*.*. Je ne trouve pas de révision contenant "old_location/.".
Asaph
@Asaph: Votre retour en arrière était ok, regardez l'historique ... je voulais direcopy ("old_location/.","new_location/");
Felix Kling
3
@dave Quand récupérerez-vous un accepté :)?
Nam G VU

Réponses:

239

Il semble que la copie ne gère que des fichiers uniques . Voici une fonction de copie récursive que j'ai trouvée dans cette note sur la page de documentation de copie :

<?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); 
} 
?>
Félix Kling
la source
2
C'est un astérisque et non une étoile;)
Gordon
6
Fonctionne comme un charme .. Merci @FelixKling
Milap
2
Pourquoi @mkdirau lieu de mkdir?
Oliboy50 du
3
@ Oliboy50: Vous pouvez demander à la personne qui a écrit le code il y a 5 ans: php.net/manual/en/function.copy.php#91010 . Il était peut-être plus populaire à l'époque de supprimer les messages d'erreur.
Felix Kling du
1
@ Oliboy50: Je vois. Je pense que cela supprime tout message d'erreur. Je ne l'ai jamais vraiment utilisé. Voici la documentation: us3.php.net/manual/en/language.operators.errorcontrol.php
Felix Kling
90

Comme décrit ici , c'est une autre approche qui prend également en charge les liens symboliques:

/**
 * 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
la source
Fonctionne très bien pour copier un dossier avec 140 sous-dossiers et chaque sous-dossier contenant 21 images. Fonctionne très bien! Merci!
Darksaint2014
1
mkdirdevrait être ajouté truecomme dernier paramètre pour prendre en charge récursivement le répertoire alors ce script est parfait
ZenithS
Cela copie le dossier entier? Que faire si vous souhaitez uniquement copier les fichiers à l' intérieur du dossier, sans le dossier parent, comme le dit la question: copy ("old_location/*.*","new_location/");cela fonctionne-t-il? Que faire si vous avez des fichiers de points, seront-ils mis en correspondance?
XCS le
35

copy () ne fonctionne qu'avec les fichiers.

La copie DOS et les commandes Unix cp seront copiées de manière récursive - donc la solution la plus rapide est simplement de décortiquer et de les utiliser. par exemple

`cp -r $src $dest`;

Sinon, vous devrez utiliser le opendir/ readdirou scandirpour lire le contenu du répertoire, parcourir les résultats et si is_dir renvoie true pour chacun d'eux, rentrer dedans.

par exemple

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
la source
1
Voici une version plus stable et plus propre de xcopy () qui ne crée pas le dossier s'il 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
Merci pour la solution backtick ! Une page qui m'a aidé à modifier la commande de copie: UNIX cp expliqué . Informations supplémentaires: PHP> = 5.3 propose de jolis itérateurs
maxpower9000
21

La meilleure solution est!

<?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
?>
noir
la source
31
Ne fonctionnera pas sur les serveurs Windows ou d'autres environnements où vous n'avez pas accès à l'un shell_execou l' autre cp. Cela en fait - à mon avis - loin d’être la «meilleure» solution.
The Pellmeister
3
En dehors de cela, les contrôles en ligne de commande à partir d'un fichier PHP peuvent être un gros problème lorsque quelqu'un trouve un moyen d'obtenir un fichier sur votre serveur.
Martijn le
A travaillé comme un charme! Sur CentOS et cela a très bien fonctionné. Merci @bstpierre
Nick Green
1
Cela ne fonctionnera pas du tout sous Windows, car il cps'agit d'une commande Linux. Pour Windows xcopy dir1 dir2 /e /i, où /esignifie copier les /irépertoires vides et ignorer les questions sur les fichiers ou les répertoires
Michel
Oui, c'est la meilleure solution si votre serveur prend en charge cette commande et que vous disposez des autorisations requises. C'est très rapide. Malheureusement, ne fonctionne pas dans tous les environnements.
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
la source
Fonctionne parfaitement! Merci bro
Robin Delaporte
8

Comme dit ailleurs, copyne fonctionne qu'avec un seul fichier pour la source et non un modèle. Si vous souhaitez copier par modèle, utilisez globpour déterminer les fichiers, puis exécutez la copie. Cependant, cela ne copiera pas les sous-répertoires, ni ne créera le répertoire de destination.

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
la source
pensez à changer: $ dest = realpath ($ dir. DIRECTORY_SEPARATOR). nom de base ($ fichier); avec: $ dest = realpath ($ dir). DIRECTORY_SEPARATOR. nom de base ($ fichier);
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 );
        }
    }
?>

à partir de la dernière quatrième ligne, faites ceci

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

et

$destination ='b';
Hemanta Nandi
la source
7

Tous mes remerciements vont à Felix Kling pour son excellente réponse que j'ai utilisée avec gratitude dans mon code. Je propose une petite amélioration d'une valeur de retour booléenne pour signaler le succès ou l'échec:

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
la source
1
vous utilisez recurse_copy () et recurseCopy () comme noms de fonction, mettez-le à jour.
AgelessEssence
Le Closedir ($ dir); L'instruction doit être en dehors de l'instruction if ($ reslut === true), c'est-à-dire une accolade plus bas. Sinon, vous risquez d'avoir des ressources non libérées.
Dimitar Darazhanski
5

Ma version élaguée de @Kzoty répond. Merci Kzoty.

Usage

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
la source
1

Je clone l'annuaire entier par 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");
Tuhin Bepari
la source
0
// using exec

function rCopy($directory, $destination)
{

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

    exec($command);

}
Łukasz Szpak
la source
0

Pour les serveurs Linux, vous n'avez besoin que d'une seule ligne de code pour copier récursivement tout en préservant les autorisations:

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

Une autre façon de procéder est:

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

mais c'est plus lent et ne préserve pas les permissions.

Dan Bray
la source
0

J'ai eu une situation similaire où je devais copier d'un domaine à un autre sur le même serveur, voici exactement ce qui a fonctionné dans mon cas, vous pouvez également ajuster en fonction du vôtre:

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

Remarquez l'utilisation de "substr ()", sans lui, la destination devient '/home/user/abcde.com/../folder/', ce que vous ne voulez peut-être pas. Donc, j'ai utilisé substr () pour éliminer les 3 premiers caractères (../) afin d'obtenir la destination souhaitée qui est '/home/user/abcde.com/folder/'. Ainsi, vous pouvez ajuster la fonction substr () ainsi que la fonction glob () jusqu'à ce qu'elle corresponde à vos besoins personnels. J'espère que cela t'aides.

Chimdi
la source