目录的递归副本

Posted

技术标签:

【中文标题】目录的递归副本【英文标题】:Recursive Copy of Directory 【发布时间】:2011-08-08 03:30:18 【问题描述】:

在我的旧 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 上它只会创建主目录,但不会将任何文件复制到其中。我不明白这 2 个 VPS 之间会发生什么变化?

【问题讨论】:

如果您缩进每个块,您的代码将更具可读性。 可能是php版本,试试php手册中的recursive copy函数 SIFE - 试过了,但也没有用。所以不确定我做错了什么。但是我只是复制代码,输入我的源和目标路径,并确保 chmod 已设置好,但它仍然不起作用 @Jasom 可能是您在目标目录中没有足够的权限复制到它。 wtf 你在用 $_POST 全局变量吗?这完全杀死了通用方法并使 func 对我无法使用 - 但无论如何,答案中有很好的例子:) 【参考方案1】:

试试这样的:

$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 ()); 感谢 OzzyCzech 非常有用的脚本......但我也想备份我的数据库,请告诉我该过程或编写脚本或给我任何链接 可能需要考虑 mkdir 上的第三个参数 true 以递归方式生成目标目录,即 mkdir($dest, 0755, true); foreach (... RecursiveIteratorIterator 是否实现了 __call() 方法,该方法将 getSubPathName() 转发到 RecursiveDirectoryIterator 的实例,或者它是如何工作的?【参考方案2】:

我是否可以建议(假设它是*nix VPS)您只需对cp -r 进行系统调用,然后让它为您复制。

【讨论】:

【参考方案3】:

我更改了 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);
    

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

【讨论】:

【参考方案4】:

Symfony's FileSystem Component 提供了良好的错误处理以及递归删除和其他有用的东西。使用@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 组件。

【讨论】:

Symfony 文件系统组件有一个mirror 方法:$filesystem->mirror('/path/to/source', '/path/to/target');【参考方案5】:

这是我们公司使用的:

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)) 检查在存在文件夹的情况下 几乎为我工作。但换成这样。 「复制($source."/".$file, $dest."/".$file);》 ⇒ 「复制($source."/".$file, $dest."/".$sourcefolder." /".$file);」【参考方案6】:

此功能非常可靠地递归复制文件夹。我从copy command of php.net的 cmets 部分复制了它

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【参考方案7】:

OzzyCheck 优雅而原始,但他忘记了最初的 mkdir($dest); 见下文。没有任何复制命令只提供内容。它必须履行其全部职责。

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

【讨论】:

你应该在创建之前检查目录是否存在。【参考方案8】:
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;

【讨论】:

【参考方案9】:

这是一个复制整个目录的简单递归函数

来源:http://php.net/manual/de/function.copy.php

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

【讨论】:

【参考方案10】:

我猜你应该检查用户(组)权限。例如,您应该考虑 chmod,具体取决于 您如何运行 (su?)PHP。您也可以选择修改您的 php 配置

【讨论】:

【参考方案11】:

嗯。因为这很复杂))

function mkdir_recursive( $dir )
  $prev = dirname($dir);
  if( ! file_exists($prev))
  
    mkdir_recursive($prev);
  
  if( ! file_exists($dir))
  
     mkdir($dir);
  


...

foreach( $files as $file)
  mkdir_recursive( dirname( $dir_d . $file));
  copy( $dir_s . $file, $dir_d . $file);

$file - 类似www/folder/ahah/file.txt

【讨论】:

【参考方案12】:

我在线程中测试的函数存在一些问题,这是一个涵盖所有内容的强大函数。亮点:

    不需要有初始或中间源目录。将处理直到源目录和复制目录的所有目录。

    能够从数组中跳过目录或文件。 (可选)使用global $skip; 跳过文件,即使在子级目录下也可以处理。

    全递归支持,支持多深度的所有文件和目录。

$from = "/path/to/source_dir";
$to = "/path/to/destination_dir";
$skip = array('some_file.php', 'somedir');

copy_r($from, $to, $skip);

function copy_r($from, $to, $skip=false) 
    global $skip;
    $dir = opendir($from);
    if (!file_exists($to)) mkdir ($to, 0775, true);
    while (false !== ($file = readdir($dir))) 
        if ($file == '.' OR $file == '..' OR in_array($file, $skip)) continue;
        
        if (is_dir($from . DIRECTORY_SEPARATOR . $file)) 
            copy_r($from . DIRECTORY_SEPARATOR . $file, $to . DIRECTORY_SEPARATOR . $file);
        
        else 
            copy($from . DIRECTORY_SEPARATOR . $file, $to . DIRECTORY_SEPARATOR . $file);
        
    
    closedir($dir);

【讨论】:

【参考方案13】:
<?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:

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

【讨论】:

【参考方案14】:

为什么不让操作系统来处理这个问题?

system("cp -r olddir newdir");

完成。

【讨论】:

不仅依赖于操作系统,而且“系统”调用通常在生产中被禁用。 不是一个好主意,除非必须,否则不要在 php 中使用 shell。

以上是关于目录的递归副本的主要内容,如果未能解决你的问题,请参考以下文章

如何恢复SVN工作副本中的ONLY目录?

bind测试递归及根区副本

javascript 相对副本(递归)

PHP 递归文件/文件夹副本

递归文件/文件夹副本

递归查找subversion工作副本并更新