递归复制目录

27

在我旧的VPS上,我使用以下代码将目录中的文件和目录复制到用户提交表单后创建的新目录中。

function copyr($source, $dest)
{
   // Simple copy for a file
   if (is_file($source)) {
      return copy($source, $dest);
   }

   // Make destination directory
   if (!is_dir($dest)) {
      mkdir($dest);
      $company = ($_POST['company']);
   }

   // Loop through the folder
   $dir = dir($source);
   while (false !== $entry = $dir->read()) {
      // Skip pointers
      if ($entry == '.' || $entry == '..') {
         continue;
      }

      // Deep copy directories
      if ($dest !== "$source/$entry") {
         copyr("$source/$entry", "$dest/$entry");
      }
   }

   // Clean up
   $dir->close();
   return true;
}

copyr('Template/MemberPages', "Members/$company")

然而,在我的新VPS上,它只会创建主目录,但不会复制任何文件到其中。我不明白在这两个VPS之间可能发生了什么变化?


如果你对每个块进行缩进,你的代码将更易读。 - Doug T.
可能是PHP版本的问题,请尝试使用PHP手册中的这个递归复制函数。 - SIFE
SIFE - 我尝试过那个,但它也没有起作用。所以我不确定我做错了什么。但我只是复制代码,输入我的源路径和目标路径,并确保 chmod 已经设置好,但它仍然无法工作。 - Jason
@Jasom,也许你没有目标目录的足够权限来将其复制。 - SIFE
你在这里使用 $_POST 全局变量干嘛?这完全破坏了通用方法,使得该函数对我来说无法使用 - 不过答案中有好的例子 :) - jebbie
15个回答

86

试试这样:

$source = "dir/dir/dir";
$dest= "dest/dir";

mkdir($dest, 0755);
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());
  }
}

迭代器遍历所有文件夹和子文件夹,并将文件从$source复制到$dest


如果您正在使用Zend Guard,请确保在使用$iterator->getSubPathName()时将其作为匿名函数调用。对我有效的方法是:call_user_func_array(array($iterator, "getSubPathName"), array()); - Someone13
感谢OzzyCzech提供的非常有用的脚本,但我还想备份我的数据库,请告诉我该过程或编写脚本或为我提供任何链接。 - Syed Arif Iqbal
2
在mkdir上考虑第三个参数为true,以递归方式创建目标目录,例如: mkdir($dest, 0755, true); foreach (...) - Andrew
1
RecursiveIteratorIterator 是否实现了 __call() 方法,以便将 getSubPathName() 转发到 RecursiveDirectoryIterator 实例中?或者它是如何工作的? - David

16

如果你使用的是*nix VPS,我建议你直接调用系统命令cp -r来进行复制。


9

我修改了Joseph的代码(如下),因为它对我来说无法工作。这是可行的代码:

function cpy($source, $dest){
    if(is_dir($source)) {
        $dir_handle=opendir($source);
        while($file=readdir($dir_handle)){
            if($file!="." && $file!=".."){
                if(is_dir($source."/".$file)){
                    if(!is_dir($dest."/".$file)){
                        mkdir($dest."/".$file);
                    }
                    cpy($source."/".$file, $dest."/".$file);
                } else {
                    copy($source."/".$file, $dest."/".$file);
                }
            }
        }
        closedir($dir_handle);
    } else {
        copy($source, $dest);
    }
}

[编辑]在创建目录之前添加了测试(第7行)


9

这个函数非常稳定地递归地复制文件夹。我从php.net的copy命令评论部分复制了它。

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

对于未来的人,我建议将此方法的主体放在if(is_dir($src)){ ..code.. }else{ copy($src, $dst); }中。这也可以防止在使用此方法复制常规文件时对您的服务器造成DoS攻击。 - vakus

7

Symfony的FileSystem组件提供良好的错误处理以及递归删除等其他有用的功能。使用@OzzyCzech的出色回答,我们可以通过以下方式进行强健的递归复制:

use Symfony\Component\Filesystem\Filesystem;

// ...

$fileSystem = new FileSystem();

if (file_exists($target))
{
    $this->fileSystem->remove($target);
}

$this->fileSystem->mkdir($target);

$directoryIterator = new \RecursiveDirectoryIterator($source, \RecursiveDirectoryIterator::SKIP_DOTS);
$iterator = new \RecursiveIteratorIterator($directoryIterator, \RecursiveIteratorIterator::SELF_FIRST);
foreach ($iterator as $item)
{
    if ($item->isDir())
    {
        $fileSystem->mkdir($target . DIRECTORY_SEPARATOR . $iterator->getSubPathName());
    }
    else
    {
        $fileSystem->copy($item, $target . DIRECTORY_SEPARATOR . $iterator->getSubPathName());
    }
}

注意:您不仅可以使用此组件,还可以将所有其他Symfony2组件作为独立组件使用。

4
Symfony文件系统组件有一个名为mirror的方法:$filesystem->mirror('/path/to/source', '/path/to/target');它可以将源目录中的所有文件和子目录复制到目标目录,并删除目标目录中在源目录中不存在的任何文件或目录。 - Silviu G

6
这是我们公司使用的技术:
static public function copyr($source, $dest)
{
    // recursive function to copy
    // all subdirectories and contents:
    if(is_dir($source)) {
        $dir_handle=opendir($source);
        $sourcefolder = basename($source);
        mkdir($dest."/".$sourcefolder);
        while($file=readdir($dir_handle)){
            if($file!="." && $file!=".."){
                if(is_dir($source."/".$file)){
                    self::copyr($source."/".$file, $dest."/".$sourcefolder);
                } else {
                    copy($source."/".$file, $dest."/".$file);
                }
            }
        }
        closedir($dir_handle);
    } else {
        // can also handle simple copy commands
        copy($source, $dest);
    }
}

在文件夹已经存在的情况下,在mkdir($dest."/".$sourcefolder);之前需要添加if (!file_exists($dest."/".$sourcefolder))检查。 - Pavel Nazarov
几乎对我有用。 但是被替换成了这样。「copy($source."/".$file, $dest."/".$sourcefolder."/".$file);」 - harufumi.abe

2
function recurse_copy($source, $dest)
{
    // 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);
    }

    // Loop through the folder
    $dir = dir($source);
    while (false !== $entry = $dir->read()) {
        // Skip pointers
        if ($entry == '.' || $entry == '..') {
            continue;
        }

        // Deep copy directories
        recurse_copy("$source/$entry", "$dest/$entry");
    }

    // Clean up
    $dir->close();
    return true;
}

2
为什么不直接要求操作系统来处理这个?
system("cp -r olddir newdir");

完成。

1
不仅在操作系统方面存在依赖性,而且在生产环境中通常禁用“系统”调用。 - user2716262
1
不是一个好主意,除非必须,否则永远不要在 PHP 中使用 shell。 - Steve Moretz

1
嗯,这很复杂))
function mkdir_recursive( $dir ){
  $prev = dirname($dir);
  if( ! file_exists($prev))
  {
    mkdir_recursive($prev);
  }
  if( ! file_exists($dir))
  {
     mkdir($dir);
  }
}

...
$srcDir = "/tmp/from"
$dstDir = "/tmp/to"
mkdir_recursive($dstDir);
foreach( $files as $file){
  copy( $srcDir."/".$file, $dstDir."/".$file);
}

$file - 类似于 file.txt 的东西


1
<?php

/**
 * code by Nk (nk.have.a@gmail.com)
 */

class filesystem
{
    public static function normalizePath($path)
    {
        return $path.(is_dir($path) && !preg_match('@/$@', $path) ? '/' : '');      
    }

    public static function rscandir($dir, $sort = SCANDIR_SORT_ASCENDING)
    {
        $results = array();

        if(!is_dir($dir))
        return $results;

        $dir = self::normalizePath($dir);

        $objects = scandir($dir, $sort);

        foreach($objects as $object)
        if($object != '.' && $object != '..')
        {
            if(is_dir($dir.$object))
            $results = array_merge($results, self::rscandir($dir.$object, $sort));
            else
            array_push($results, $dir.$object);
        }

        array_push($results, $dir);

        return $results;
    }

    public static function rcopy($source, $dest, $destmode = null)
    {
        $files = self::rscandir($source);

        if(empty($files))
        return;

        if(!file_exists($dest))
        mkdir($dest, is_int($destmode) ? $destmode : fileperms($source), true);

        $source = self::normalizePath(realpath($source));
        $dest = self::normalizePath(realpath($dest));

        foreach($files as $file)
        {
            $file_dest = str_replace($source, $dest, $file);

            if(is_dir($file))
            {
                if(!file_exists($file_dest))
                mkdir($file_dest, is_int($destmode) ? $destmode : fileperms($file), true);
            }
            else
            copy($file, $file_dest);
        }
    }
}

?>

/var/www/websiteA/backup.php :

/var/www/websiteA/backup.php:

<?php /* include.. */ filesystem::rcopy('/var/www/websiteA/', '../websiteB'); ?>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接