Скопируйте все содержимое одного каталога в другой, используя php


146

Я попытался скопировать все содержимое каталога в другое место, используя

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

но он говорит, что не может найти поток, истина *.*не найдена.

Любым другим путем

Спасибо Дэйв


1
@ редакторы: Вы уверены, что это "old_location/."была просто опечатка?
Феликс Клинг

У Рича Родекера в блоге есть сценарий, который, похоже, делает именно это. visible-form.com/blog/copy-directory-in-php
Джон Ф Хэнкок

@ Феликс: Мне было интересно то же самое. Я откатился на первую ревизию но она есть "old_location/*.*. Я не могу найти ревизию, содержащую "old_location/.".
Асаф

@Asaph: Ваш откат был в порядке, посмотрите на историю ... Я имел в видуcopy ("old_location/.","new_location/");
Феликс Клинг

3
@dave Когда ты найдешь принятого :)?
Nam G VU

Ответы:


239

Кажется, что копия обрабатывает только отдельные файлы . Вот функция для рекурсивного копирования, которую я нашел в этой заметке на странице документации по копированию :

<?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
Это звездочка, а не звезда;)
Гордон

6
Работает как шарм .. Спасибо @FelixKling
Милап

2
Почему @mkdirвместо mkdir?
Oliboy50

3
@ Oliboy50: Вы можете спросить человека, который написал код 5 лет назад: php.net/manual/en/function.copy.php#91010 . Возможно, тогда было популярнее подавлять сообщения об ошибках.
Феликс Клинг

1
@ Oliboy50: Понятно. Я думаю, что это подавляет любое сообщение об ошибке. Я действительно никогда не использовал это все же. Это документация: us3.php.net/manual/en/language.operators.errorcontrol.php
Феликс Клинг

90

Как описано здесь , это еще один подход, который также заботится о символических ссылках:

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

Отлично работало, чтобы скопировать папку с 140 подпапками и каждой подпапкой, содержащей 21 изображение. Прекрасно работает! Спасибо!
Darksaint2014

1
mkdirдолжен быть добавлен trueв качестве последнего параметра для поддержки рекурсивной директории, тогда этот скрипт идеален
ZenithS

Это копирует всю папку? Что если вы хотите скопировать файлы только внутри папки, без родительской папки, как указано в вопросе: copy ("old_location/*.*","new_location/");это работает? Что если у вас есть точечные файлы, будут ли они сопоставлены?
XCS

35

copy () работает только с файлами.

Обе команды DOS copy и Unix cp будут копировать рекурсивно, поэтому самое быстрое решение - просто раскошелиться и использовать их. например

`cp -r $src $dest`;

В противном случае вам нужно будет использовать opendir/ readdirили, scandirчтобы прочитать содержимое каталога, перебрать результаты, и если is_dir вернет true для каждого из них, вернитесь в него.

например

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
Вот более стабильная и более чистая версия xcopy (), которая не создает папку, если она существует: 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

Спасибо за обратное решение ! Страница, которая помогла мне настроить команду копирования: UNIX cp объяснил . Дополнительная информация: PHP> = 5.3 предлагает несколько хороших итераторов
maxpower9000

21

Лучшее решение!

<?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
Не будет работать на серверах Windows , или других средах , где вы либо не имеют доступа к любому shell_execили cp. Это делает это - по моему мнению - едва ли "лучшим" решением.
Pellmeister

3
Кроме того, управление командной строкой из файла PHP может быть большой проблемой, когда кто-то находит способ получить файл на вашем сервере.
Мартейн

Работал как шарм! На CentOS и он работал отлично. Спасибо @bstpierre
Ник Грин

1
Это не будет работать на Windows вообще, потому что cpэто команда Linux. Для использования в Windows xcopy dir1 dir2 /e /i, где /eозначает копирование пустых папок и /iигнорирование вопросов о файлах или каталогах
Мишель

Да, это лучшее решение, если ваш сервер поддерживает эту команду и у вас есть необходимые разрешения. Это очень быстро. К сожалению, не работает во всех средах.
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 );
    }
}

Работает отлично! Спасибо братан
Робин Делапорте

8

Как сказано в другом месте, copyработает только с одним файлом для источника, а не шаблон. Если вы хотите копировать по шаблону, используйте globдля определения файлов, затем запустите копирование. Это не скопирует подкаталоги, хотя и не создаст каталог назначения.

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

рассмотрите возможность изменения: $ dest = realpath ($ dir. DIRECTORY_SEPARATOR). Базовое имя ($ файла); с: $ dest = realpath ($ dir). DIRECTORY_SEPARATOR. Базовое имя ($ файла);
Давез

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

из последней 4-й строки, сделайте это

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

и

$destination ='b';

7

Я должен поблагодарить Феликса Клинга за отличный ответ, который я с благодарностью использовал в своем коде. Я предлагаю небольшое улучшение логического возвращаемого значения, чтобы сообщить об успехе или неудаче:

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
Вы используете recurse_copy () и recurseCopy () в качестве имен функций, обновите их.
AgelessEssence

Закрытый ($ dir); оператор должен находиться за пределами оператора if ($ reslut === true), то есть на одну фигурную скобку ниже. В противном случае вы рискуете получить не освобожденные ресурсы.
Димитар Даражанский


5

Моя обрезанная версия ответа @Kzoty. Спасибо, Кзоты.

использование

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

Я клонирую весь каталог с помощью 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

Для серверов Linux вам просто нужна одна строка кода для рекурсивного копирования с сохранением разрешения:

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

Еще один способ сделать это:

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

но это медленнее и не сохраняет разрешения.


0

У меня была похожая ситуация, когда мне нужно было копировать из одного домена в другой на том же сервере. Вот именно то, что сработало в моем случае, вы также можете настроить в соответствии с вашим:

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

Обратите внимание на использование «substr ()», без которого местом назначения становится «/home/user/abcde.com/../folder/», что может быть тем, что вам не нужно. Итак, я использовал substr (), чтобы убрать первые 3 символа (../), чтобы получить желаемый пункт назначения, который называется /home/user/abcde.com/folder/. Таким образом, вы можете настроить функцию substr (), а также функцию glob (), пока она не будет соответствовать вашим личным потребностям. Надеюсь это поможет.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.