首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 文件複製:copy()、stream_copy_to_stream、大文件分塊、權限保持

     編輯:PHP 綜合知識

核心函數對比

函數適用場景性能特性
copy($src, $dst)中小文件 (<100MB)、簡單複製⚡⚡⚡ (C 級內部優化)自動創建目標目錄? 不會;覆蓋存在文件
stream_copy_to_stream($src, $dst)大文件、流式、可控緩衝⚡⚡⚡ (內核零拷貝機會)需手動 fopen、支援任意流包裝器
file_put_contents($dst, file_get_contents($src))極小文件、簡潔🐢 (全量進內存)不適合大文件
shell_exec('cp -p ...')需保持屬性/權限/時間戳⚡⚡ (系統調用)依賴 OS、Windows 不通用

1. 基礎複製 (copy)

<?php
$src = 'image/a1.jpg';
$dst = 'backup/a1.jpg';

if (!file_exists($src)) {
    die("源文件不存在: $src");
}

// 確保目標目錄存在
$dstDir = dirname($dst);
if (!is_dir($dstDir)) {
    mkdir($dstDir, 0755, true);
}

if (copy($src, $dst)) {
    echo "複製成功: $dst\n";
    // 可選:保持原文件時間
    touch($dst, filemtime($src));
} else {
    echo "複製失敗\n";
}

2. 大文件流式複製 (stream_copy_to_stream)

<?php
function copyLargeFile(string $src, string $dst, int $bufferSize = 8192): bool
{
    if (!file_exists($src)) return false;
    
    $dstDir = dirname($dst);
    if (!is_dir($dstDir)) mkdir($dstDir, 0755, true);
    
    $in = fopen($src, 'rb');
    $out = fopen($dst, 'wb');
    
    if (!$in || !$out) {
        $in && fclose($in);
        $out && fclose($out);
        return false;
    }
    
    // 可選:非阻塞/異步 (需 stream_set_blocking)
    $bytes = stream_copy_to_stream($in, $out, $bufferSize);
    
    fclose($in);
    fclose($out);
    
    // 保持元數據
    if ($bytes !== false) {
        $stat = stat($src);
        touch($dst, $stat['mtime'], $stat['atime']);
        chmod($dst, $stat['mode'] & 0777);
        chown($dst, $stat['uid']);
        chgrp($dst, $stat['gid']);
        return true;
    }
    return false;
}

// 使用
copyLargeFile('/data/video/large.mp4', '/backup/video/large.mp4');

3. 異步/非阻塞複製 (Swoole/RoadRunner 環境)

<?php
// Swoole 異步文件複製 (不阻塞 Worker)
function asyncCopy(string $src, string $dst, callable $callback): void
{
    Swoole\Async::copy($src, $dst, function (bool $success) use ($callback) {
        $callback($success);
    });
}

// 使用
asyncCopy('/big/file.iso', '/backup/file.iso', function ($ok) {
    Log::info($ok ? '異步複製完成' : '異步複製失敗');
});
// Worker 可繼續處理其他請求

4. 目錄遞歸複製

<?php
function copyDir(string $src, string $dst, bool $preserveMeta = true): bool
{
    if (!is_dir($src)) return false;
    if (!is_dir($dst)) mkdir($dst, 0755, true);
    
    $items = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($src, FilesystemIterator::SKIP_DOTS),
        RecursiveIteratorIterator::SELF_FIRST
    );
    
    foreach ($items as $item) {
        $relPath = $items->getSubPathName();
        $target = $dst . DIRECTORY_SEPARATOR . $relPath;
        
        if ($item->isDir()) {
            if (!is_dir($target)) mkdir($target, 0755, true);
            if ($preserveMeta) {
                touch($target, $item->getMTime());
                chmod($target, $item->getPerms() & 0777);
            }
        } else {
            copy($item->getPathname(), $target);
            if ($preserveMeta) {
                touch($target, $item->getMTime());
                chmod($target, $item->getPerms() & 0777);
            }
        }
    }
    return true;
}

5. 跨服務器複製 (SFTP/SSH2)

<?php
// 需 php-ssh2 擴展
function copyRemote(string $host, int $port, string $user, string $pass, string $remoteSrc, string $localDst): bool
{
    $conn = ssh2_connect($host, $port);
    if (!$conn) return false;
    if (!ssh2_auth_password($conn, $user, $pass)) return false;
    
    $sftp = ssh2_sftp($conn);
    $remoteStream = fopen("ssh2.sftp://{$sftp}{$remoteSrc}", 'rb');
    $localStream = fopen($localDst, 'wb');
    
    if (!$remoteStream || !$localStream) return false;
    
    $ok = stream_copy_to_stream($remoteStream, $localStream);
    fclose($remoteStream);
    fclose($localStream);
    return $ok !== false;
}

常見錯誤與排查

錯誤原因修復
failed to open stream: Permission denied目標目錄不可寫chown www:www /target/dir、chmod 755
copy(): The first argument to copy() function cannot be a directory誤傳目錄使用 copyDir() 遞歸
大文件複製中斷PHP max_execution_time/內存限制用 stream_copy_to_stream + set_time_limit(0)
Windows 路徑分隔符反斜杠轉義用 DIRECTORY_SEPARATOR 或正斜杠 / (PHP 自動兼容)

總結

文件複製選型:<100MB 用 copy()、>100MB 用 stream_copy_to_stream()、需保持屬性用 stat()+touch()+chmod()、異步環境用 Swoole Async、跨機器用 SSH2 SFTP。始終檢查返回值、處理異常、確保目標目錄存在。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved