Copie todo o conteúdo de um diretório para outro usando php

146

Tentei copiar todo o conteúdo do diretório para outro local usando

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

mas diz que não consegue encontrar o fluxo, true *.*não foi encontrado.

Qualquer outra maneira

Graças Dave

Dave
fonte
1
@os editores: Você tem certeza de que "old_location/."foi apenas um erro de digitação?
Felix Kling
Rich Rodecker tem um script em seu blog que parece fazer exatamente isso. visible-form.com/blog/copy-directory-in-php
Jon F Hancock
@ Felix: Eu estava pensando a mesma coisa. Voltei para a primeira revisão, mas ela tem "old_location/*.*. Não consigo encontrar uma revisão contendo "old_location/.".
Asaph
@Asaph: Seu reversão foi ok, olhar para a história ... eu quis dizercopy ("old_location/.","new_location/");
Felix Kling
3
@dave Quando você receberá um :) aceito?
Nam G VU

Respostas:

239

Parece que a cópia única lidar com arquivos individuais . Aqui está uma função para copiar recursivamente que encontrei nesta nota na página de documentação da cópia :

<?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
fonte
2
É um asterisco e não uma estrela;)
Gordon
6
Funciona como um encanto .. Obrigado @FelixKling
Milap
2
Por que ao @mkdirinvés de mkdir?
Oliboy50
3
@ Oliboy50: Você pode perguntar à pessoa que escreveu o código há 5 anos: php.net/manual/en/function.copy.php#91010 . Talvez fosse mais popular na época suprimir mensagens de erro.
Felix Kling
1
@ Oliboy50: Entendo. Eu acho que suprime qualquer mensagem de erro. Eu nunca realmente o usei. Esta é a documentação: us3.php.net/manual/en/language.operators.errorcontrol.php
Felix Kling
90

Conforme descrito aqui , essa é outra abordagem que também cuida dos links 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
fonte
Funcionou muito bem para copiar uma pasta com 140 subpastas e cada subpasta contendo 21 imagens. Funciona bem! Obrigado!
precisa saber é o seguinte
1
mkdirdeve ser adicionado truecomo o último parâmetro para apoiar diretório de forma recursiva, em seguida, esse script é perfeito
CénitS
Isso copia a pasta inteira? E se você quiser apenas copiar os arquivos dentro da pasta, sem a pasta pai, como a pergunta diz: copy ("old_location/*.*","new_location/");Isso funciona? E se você tiver arquivos de ponto, eles serão correspondidos?
XCS
35

copy () funciona apenas com arquivos.

Os comandos DOS copy e Unix cp serão copiados recursivamente - portanto, a solução mais rápida é apenas descartar e usá-los. por exemplo

`cp -r $src $dest`;

Caso contrário, você precisará usar o opendir/ readdirou scandirpara ler o conteúdo do diretório, percorrer os resultados e se is_dir retornar true para cada um, recursá-lo.

por exemplo

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
fonte
1
Aqui está uma versão mais estável e mais limpa do xcopy () que não cria a pasta, se existir: 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
Obrigado pela solução backtick ! Uma página que me ajudou a ajustar o comando copy: o UNIX cp explicou . Informações adicionais: PHP> = 5.3 oferece algumas agradáveis iteradores
maxpower9000
21

A melhor solução é!

<?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
?>
Preto
fonte
31
Não funcionará em servidores Windows ou em outros ambientes nos quais você não tem acesso shell_execou cp. Isso torna - na minha opinião - dificilmente a "melhor" solução.
The Pellmeister
3
Além disso, os controles da linha de comando de um arquivo PHP podem ser um grande problema quando alguém encontra uma maneira de obter um arquivo no seu servidor.
287
Trabalhou como um encanto! No CentOS e funcionou muito bem. Obrigado @bstpierre
Nick Green
1
Isso não funcionará no Windows, porque cpé um comando do Linux. Para o uso do Windows xcopy dir1 dir2 /e /i, onde /eestá para a cópia diretórios vazios e /ipara ignorar perguntas sobre arquivos ou diretórios
Michel
Sim, é a melhor solução se o servidor suportar esse comando e você tiver as permissões necessárias. É muito rápido. Infelizmente não está funcionando em todos os ambientes.
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
fonte
Funciona perfeitamente! Obrigado, manoel
Robin Delaporte
8

Como dito em outro lugar, copysó funciona com um único arquivo de origem e não com um padrão. Se você deseja copiar por padrão, use globpara determinar os arquivos e execute a cópia. Porém, isso não copiará subdiretórios, nem criará o diretório 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
fonte
considere alterar: $ dest = caminho real ($ dir. DIRECTORY_SEPARATOR). nome de base (arquivo $); com: $ dest = caminho real ($ dir). DIRECTORY_SEPARATOR. nome de base (arquivo $);
Davidez19 /
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 );
        }
    }
?>

da última quarta linha, faça isso

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

e

$destination ='b';
Hemanta Nandi
fonte
7

Devo agradecer a Felix Kling por sua excelente resposta, que usei com gratidão no meu código. Ofereço um pequeno aprimoramento de um valor de retorno booleano para relatar sucesso ou falha:

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
fonte
1
você está usando recurse_copy () e recurseCopy () como nomes de função, atualize-o.
AgelessEssence
O closedir ($ dir); A instrução precisa estar fora da instrução if ($ reslut === true), ou seja, uma chave mais abaixo. Caso contrário, você corre o risco de ter recursos não liberados.
Dimitar Darazhanski
5

Minha versão podada da resposta @Kzoty. Obrigado 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
fonte
1

Eu clono o diretório inteiro pelo 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
fonte
0
// using exec

function rCopy($directory, $destination)
{

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

    exec($command);

}
Łukasz Szpak
fonte
0

Para servidores Linux, você só precisa de uma linha de código para copiar recursivamente, preservando a permissão:

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

Outra maneira de fazer isso é:

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

mas é mais lento e não preserva permissões.

Dan Bray
fonte
0

Eu tive uma situação semelhante em que precisava copiar de um domínio para outro no mesmo servidor. Aqui está exatamente o que funcionou no meu caso, você também pode ajustar para se adequar ao seu:

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

Observe o uso de "substr ()", sem ele, o destino se torna '/home/user/abcde.com/../folder/', que pode ser algo que você não deseja. Então, usei substr () para eliminar os três primeiros caracteres (../) para obter o destino desejado que é '/home/user/abcde.com/folder/'. Assim, você pode ajustar a função substr () e também a função glob () até que ela atenda às suas necessidades pessoais. Espero que isto ajude.

Chimdi
fonte