【问题标题】:Copy entire contents of a directory to another using php使用php将目录的全部内容复制到另一个目录
【发布时间】:2011-01-04 06:49:49
【问题描述】:

我尝试使用

将目录的全部内容复制到另一个位置
copy ("old_location/*.*","new_location/");

但它说它找不到流,真正的*.* 没有找到。

其他方式

谢谢 戴夫

【问题讨论】:

  • @the editors: 你确定"old_location/." 只是一个错字吗?
  • Rich Rodecker 在他的博客上有一个脚本似乎就是这样做的。 visible-form.com/blog/copy-directory-in-php
  • @Felix:我也在想同样的事情。我回滚到第一个修订版,但它有"old_location/*.*。我找不到包含 "old_location/." 的修订版。
  • @Asaph:你的回滚没问题,看看历史......我的意思是copy ("old_location/.","new_location/");
  • @dave 你什么时候会收到一个被接受的:)?

标签: php


【解决方案1】:

适用于一级目录。对于具有多级目录的文件夹,我使用了这个:

function recurseCopy(
    string $sourceDirectory,
    string $destinationDirectory,
    string $childFolder = ''
): void {
    $directory = opendir($sourceDirectory);

    if (is_dir($destinationDirectory) === false) {
        mkdir($destinationDirectory);
    }

    if ($childFolder !== '') {
        if (is_dir("$destinationDirectory/$childFolder") === false) {
            mkdir("$destinationDirectory/$childFolder");
        }

        while (($file = readdir($directory)) !== false) {
            if ($file === '.' || $file === '..') {
                continue;
            }

            if (is_dir("$sourceDirectory/$file") === true) {
                recurseCopy("$sourceDirectory/$file", "$destinationDirectory/$childFolder/$file");
            } else {
                copy("$sourceDirectory/$file", "$destinationDirectory/$childFolder/$file");
            }
        }

        closedir($directory);

        return;
    }

    while (($file = readdir($directory)) !== false) {
        if ($file === '.' || $file === '..') {
            continue;
        }

        if (is_dir("$sourceDirectory/$file") === true) {
            recurseCopy("$sourceDirectory/$file", "$destinationDirectory/$file");
        }
        else {
            copy("$sourceDirectory/$file", "$destinationDirectory/$file");
        }
    }

    closedir($directory);
}

【讨论】:

【解决方案2】:

作为described here,这是另一种处理符号链接的方法:

/**
 * 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 个图像的文件夹非常有用。效果很好!谢谢!
  • mkdir 应该添加true 作为支持递归目录的最后一个参数,那么这个脚本是完美的
  • 这会复制整个文件夹吗?如果您只想复制 inside 文件夹中的文件,而不想复制父文件夹,如问题所述:copy ("old_location/*.*","new_location/"); 这行得通吗?如果你有点文件,它们会被匹配吗?
【解决方案3】:

copy() 仅适用于文件。

DOS 复制和 Unix cp 命令都将递归复制 - 所以最快的解决方案就是使用这些命令。例如

`cp -r $src $dest`;

否则,您需要使用opendir/readdirscandir 来读取目录的内容,遍历结果,如果 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);
        }
    }
}

【讨论】:

  • 这是一个更稳定、更干净的 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 != '.' &amp;&amp; $file != '..') { if (is_dir($srcfile)) { if (!file_exists($destfile)) { mkdir($destfile); } xcopy($srcfile, $destfile); } else { copy($srcfile, $destfile); } } } }
  • 感谢反引号解决方案!帮助我调整复制命令的页面:UNIX cp explained。附加信息:PHP >=5.3 提供了一些不错的 iterators
【解决方案4】:

最好的解决方案是!

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

【讨论】:

  • 不适用于 Windows 服务器或其他您无法访问 shell_execcp 的环境。这使得它 - 在我看来 - 几乎不是“最佳”解决方案。
  • 除此之外,当有人想办法在您的服务器上获取文件时,来自 PHP 文件的命令行控件可能会成为一个大问题。
  • 工作就像一个魅力!在 CentOS 上,它工作得很好。谢谢@bstpierre
  • 这在 Windows 上根本不起作用,因为 cp 是一个 Linux 命令。对于 Windows,使用 xcopy dir1 dir2 /e /i ,其中 /e 代表复制空目录,/i 代表忽略有关文件或目录的问题
  • 是的,如果您的服务器支持此命令并且您具有所需的权限,这是最好的解决方案。它非常快。不幸的是,不适用于所有环境。
【解决方案5】:
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 );
    }
}

【讨论】:

  • 完美运行!谢谢兄弟
【解决方案6】:

使用 Symfony,这很容易实现:

$fileSystem = new Symfony\Component\Filesystem\Filesystem();
$fileSystem->mirror($from, $to);

https://symfony.com/doc/current/components/filesystem.html

【讨论】:

    【解决方案7】:

    就像在别处所说的那样,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 ) 。目录分隔符。基本名称($file);
    【解决方案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';
    

    【讨论】:

      【解决方案9】:

      非常感谢 Felix Kling 的出色回答,我非常感激地在我的代码中使用了该回答。我提供了一个布尔返回值的小增强来报告成功或失败:

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

      【讨论】:

      • 你正在使用 recurse_copy() 和 recurseCopy() 作为函数名,更新它。
      • 关闭的ir($dir);语句需要在 if($reslut=== true) 语句之外,即一个大括号再往下。否则,您将面临资源未释放的风险。
      【解决方案10】:

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

      【讨论】:

        【解决方案11】:

        我通过 SPL 目录迭代器克隆整个目录。

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

        【讨论】:

          【解决方案12】:

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

          但速度较慢且不保留权限。

          【讨论】:

            【解决方案13】:

            我有类似的情况,我需要在同一台服务器上从一个域复制到另一个域,这正是我的情况,您也可以调整以适合您的情况:

            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() 函数,直到它满足您的个人需求。希望这会有所帮助。

            【讨论】:

              【解决方案14】:

              基于此处大部分答案的部分内容,带有返回日志记录的冗长注释示例:

              它被呈现为一个静态类方法,但也可以作为一个简单的函数工作:

              /**
               * Recursive copy directories and content
               * 
               * @link        https://stackoverflow.com/a/2050909/591486
               * @since       4.7.2
              */
              public static function copy_recursive( $source = null, $destination = null, &$log = [] ) {
              
                  // is directory ##
                  if ( is_dir( $source ) ) {
              
                      $log[] = 'is_dir: '.$source;
              
                      // log results of mkdir call ##
                      $log[] = '@mkdir( "'.$destination.'" ): '.@mkdir( $destination );
              
                      // get source directory contents ##
                      $source_directory = dir( $source );
              
                      // loop over items in source directory ##
                      while ( FALSE !== ( $entry = $source_directory->read() ) ) {
                          
                          // skip hidden ##
                          if ( $entry == '.' || $entry == '..' ) {
              
                              $log[] = 'skip hidden entry: '.$entry;
              
                              continue;
              
                          }
              
                          // get full source "entry" path ##
                          $source_entry = $source . '/' . $entry; 
              
                          // recurse for directories ##
                          if ( is_dir( $source_entry ) ) {
              
                              $log[] = 'is_dir: '.$source_entry;
              
                              // return to self, with new arguments ##
                              self::copy_recursive( $source_entry, $destination.'/'.$entry, $log );
              
                              // break out of loop, to stop processing ##
                              continue;
              
                          }
              
                          $log[] = 'copy: "'.$source_entry.'" --> "'.$destination.'/'.$entry.'"';
              
                          // copy single files ##
                          copy( $source_entry, $destination.'/'.$entry );
              
                      }
              
                      // close connection ##
                      $source_directory->close();
              
                  } else {
              
                      $log[] = 'copy: "'.$source.'" --> "'.$destination.'"';
              
                      // plain copy, as $destination is a file ##
                      copy( $source, $destination );
              
                  }
              
                  // clean up log ##
                  $log = array_unique( $log );
              
                  // kick back log for debugging ##
                  return $log;
              
              }
              

              调用方式:

              // call method ##
              $log = \namespace\to\method::copy_recursive( $source, $destination );
              
              // write log to error file - you can also just dump it on the screen ##
              error_log( var_export( $log, true ) );
              

              【讨论】:

                【解决方案15】:
                // using exec
                
                function rCopy($directory, $destination)
                {
                
                    $command = sprintf('cp -r %s/* %s', $directory, $destination);
                
                    exec($command);
                
                }
                

                【讨论】:

                  【解决方案16】:

                  要将整个目录从一个目录复制到另一个目录,首先您应该确定传输文件是否正确传输。为此,我们一一使用复制文件!在正确的目录中。我们复制一个文件并检查它是否为真转到下一个文件并继续...

                  1- 我用这个函数检查传输每个文件的安全过程:

                  
                  function checksum($src,$dest)
                  {
                      if(file_exists($src) and file_exists($dest)){
                          return md5_file($src) == md5_file($dest) ? true : false;
                      }else{
                          return false;
                      }
                  }
                  
                  

                  2- 现在我将文件从 src 一个一个复制到 dest,检查它然后继续。 (对于我不想复制的文件夹,请使用排除数组)

                  $src  = __DIR__ . '/src';
                  $dest = __DIR__ . '/dest';
                  $exclude = ['.', '..'];
                  
                  function copyDir($src, $dest, $exclude)
                  {
                  
                      !is_dir($dest) ? mkdir($dest) : '';
                  
                      foreach (scandir($src) as $item) {
                  
                          $srcPath = $src . '/' . $item;
                          $destPath = $dest . '/' . $item;
                  
                          if (!in_array($item, $exclude)) {
                  
                              if (is_dir($srcPath)) {
                  
                                  copyDir($srcPath, $destPath, $exclude);
                  
                              } else {
                  
                                  copy($srcPath, $destPath);
                  
                                  if (checksum($srcPath, $destPath)) {
                                      echo 'Success transfer for:' . $srcPath . '<br>';
                                  }else{
                                      echo 'Failed transfer for:' . $srcPath . '<br>';
                                  }
                              }
                          }
                      }
                  
                  }
                  

                  【讨论】:

                  • 没有任何解释的代码很少有帮助。 Stack Overflow 是关于学习的,而不是提供 sn-ps 来盲目复制和粘贴。请编辑您的问题并解释它如何回答所提出的具体问题。见How to Answer
                  猜你喜欢
                  • 1970-01-01
                  • 2011-09-07
                  • 2023-03-05
                  • 1970-01-01
                  • 2014-07-18
                  • 2020-09-06
                  • 2013-01-11
                  • 1970-01-01
                  相关资源
                  最近更新 更多