คัดลอกเนื้อหาทั้งหมดของไดเรกทอรีไปที่อื่นโดยใช้ php


146

ฉันพยายามคัดลอกเนื้อหาทั้งหมดของไดเรกทอรีไปยังตำแหน่งอื่นโดยใช้

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

แต่มันบอกว่าหากระแส*.*ไม่พบไม่พบความจริง

วิธีอื่น ๆ

ขอบคุณเดฟ


1
@ บรรณาธิการ: คุณแน่ใจหรือว่า"old_location/."เป็นเพียงการพิมพ์ผิด?
เฟลิกซ์คลิง

Rich Rodecker มีสคริปต์ในบล็อกของเขาที่ดูเหมือนจะทำเช่นนั้น visible-form.com/blog/copy-directory-in-php
Jon F Hancock

@ เฟลิกซ์: ฉันสงสัยในสิ่งเดียวกัน ผมย้อนกลับไปแก้ไขครั้งแรก "old_location/*.*แต่มันก็มี "old_location/."ฉันไม่สามารถหาการแก้ไขที่มี
Asaph

@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
มันเป็นเครื่องหมายดอกจันและไม่ใช่ดาว;)
Gordon

6
ทำงานเหมือนมีเสน่ห์ .. ขอบคุณ @FelixKling
Milap

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
Felix Kling

90

ตามที่อธิบายไว้ที่นี่นี่เป็นวิธีการอื่นที่ดูแล symlink ด้วย:

/**
 * 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

คัดลอก () ใช้งานได้กับไฟล์เท่านั้น

ทั้งคำสั่ง DOS copy และ Unix cp จะทำการคัดลอกซ้ำ - ดังนั้นทางออกที่รวดเร็วที่สุดคือการใช้งานเชลล์ เช่น

`cp -r $src $dest`;

มิฉะนั้นคุณจะต้องใช้opendir/ readdirหรือscandirเพื่ออ่านเนื้อหาของไดเรกทอรีทำซ้ำผ่านผลลัพธ์และหาก is_dir คืนค่าจริงสำหรับแต่ละรายการให้เรียกเก็บเงินคืน

เช่น

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

ขอบคุณสำหรับโซลูชัน backtick ! หน้าเว็บที่ช่วยให้ผมปรับแต่งคำสั่งคัดลอก: 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
นอกจากนั้นการควบคุม commandline จากไฟล์ PHP อาจเป็นปัญหาใหญ่เมื่อมีคนหาวิธีในการรับไฟล์บนเซิร์ฟเวอร์ของคุณ
Martijn

ทำงานเหมือนจับใจ! บน CentOS และมันใช้งานได้ดี ขอบคุณ @bstpierre
Nick Green

1
สิ่งนี้จะไม่ทำงานบน Windows เลยเพราะcpเป็นคำสั่ง Linux สำหรับการใช้งาน Windows xcopy dir1 dir2 /e /iซึ่ง/eหมายถึงการทำสำเนา dirs ที่ว่างเปล่าและ/iสำหรับการเพิกเฉยต่อคำถามเกี่ยวกับไฟล์หรือไดเรกทอรี
Michel

ใช่มันเป็นทางออกที่ดีที่สุดหากเซิร์ฟเวอร์ของคุณรองรับคำสั่งนี้และคุณต้องได้รับอนุญาต มันเร็วมาก น่าเสียดายที่ไม่สามารถใช้งานได้ในทุกสภาพแวดล้อม
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 );
    }
}

ทำงานได้อย่างสมบูรณ์แบบ! ขอบคุณครับ
Robin Delaporte

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) basename ($ ไฟล์); ด้วย: $ dest = realpath ($ dir) DIRECTORY_SEPARATOR basename ($ ไฟล์);
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 );
        }
    }
?>

จากบรรทัดที่ 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

The closir ($ dir); คำสั่งจะต้องอยู่นอกคำสั่ง if ($ reslut === จริง) คือหนึ่งวงเล็บปีกกาต่อไปลง มิฉะนั้นคุณอาจเสี่ยงต่อการมีทรัพยากรที่ไม่ใช่อิสระ
Dimitar Darazhanski


5

@Kzoty เวอร์ชันที่ตัดทิ้งแล้วของฉัน ขอบคุณ 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 () ฟังก์ชั่นจนกว่ามันจะเหมาะกับความต้องการส่วนบุคคลของคุณ หวังว่านี่จะช่วยได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.