当前位置:首页 > PHP

php如何实现下载进度

2026-02-14 04:47:33PHP

使用 cURL 获取下载进度

在 PHP 中,可以通过 cURL 的 CURLOPT_PROGRESSFUNCTIONCURLOPT_NOPROGRESS 选项来实现下载进度跟踪。需要启用进度回调函数并关闭默认的进度显示。

$url = 'https://example.com/largefile.zip';
$file = fopen('downloaded.zip', 'w');

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_FILE, $file);
curl_setopt($ch, CURLOPT_NOPROGRESS, false);
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION, function(
    $resource, $download_size, $downloaded, $upload_size, $uploaded
) {
    if ($download_size > 0) {
        $progress = round(($downloaded / $download_size) * 100, 2);
        echo "Progress: {$progress}%\n";
    }
});
curl_exec($ch);
curl_close($ch);
fclose($file);

使用输出缓冲实时显示进度

对于需要网页实时显示进度的情况,可以结合输出缓冲和 flush 函数。这种方法适用于浏览器端的进度显示。

ob_start();
$url = 'https://example.com/largefile.zip';
$file = fopen('downloaded.zip', 'w');

$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_FILE => $file,
    CURLOPT_NOPROGRESS => false,
    CURLOPT_PROGRESSFUNCTION => function(
        $resource, $download_size, $downloaded, $upload_size, $uploaded
    ) {
        if ($download_size > 0) {
            $progress = round(($downloaded / $download_size) * 100, 2);
            echo "<script>updateProgress({$progress})</script>";
            ob_flush();
            flush();
        }
    }
]);
curl_exec($ch);
curl_close($ch);
fclose($file);
ob_end_clean();

分段下载与进度存储

对于大文件下载,可以考虑分段下载并将进度信息存储到数据库或文件中,以便中断后恢复下载。

$url = 'https://example.com/largefile.zip';
$file_path = 'downloaded.zip';
$progress_file = 'progress.json';

// 读取之前的进度
$progress = file_exists($progress_file) ? json_decode(file_get_contents($progress_file), true) : [
    'downloaded' => 0,
    'total_size' => 0
];

$ch = curl_init();
curl_setopt_array($ch, [
    CURLOPT_URL => $url,
    CURLOPT_RANGE => $progress['downloaded'] . '-',
    CURLOPT_WRITEFUNCTION => function($ch, $data) use ($file_path, $progress_file, &$progress) {
        $length = fwrite(fopen($file_path, 'a'), $data);
        $progress['downloaded'] += $length;

        if ($progress['total_size'] == 0) {
            $progress['total_size'] = curl_getinfo($ch, CURLINFO_CONTENT_LENGTH_DOWNLOAD);
        }

        file_put_contents($progress_file, json_encode($progress));
        return $length;
    }
]);
curl_exec($ch);
curl_close($ch);

使用 PHP 内置流包装器

PHP 的流包装器也可以用于实现简单的下载进度跟踪,虽然功能不如 cURL 全面。

$url = 'https://example.com/largefile.zip';
$context = stream_context_create([], [
    'notification' => function($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max) {
        if ($notification_code == STREAM_NOTIFY_PROGRESS && $bytes_max > 0) {
            $progress = round(($bytes_transferred / $bytes_max) * 100, 2);
            echo "Progress: {$progress}%\n";
        }
    }
]);

file_put_contents('downloaded.zip', fopen($url, 'r', false, $context));

结合 AJAX 实现网页进度显示

对于网页应用,可以结合前端 AJAX 和后端会话来实现实时进度显示。

后端 PHP (download.php):

session_start();
$url = 'https://example.com/largefile.zip';
$_SESSION['download_progress'] = ['total' => 0, 'current' => 0];

$ch = curl_init($url);
curl_setopt_array($ch, [
    CURLOPT_FILE => fopen('downloaded.zip', 'w'),
    CURLOPT_NOPROGRESS => false,
    CURLOPT_PROGRESSFUNCTION => function($ch, $download_size, $downloaded, $upload_size, $uploaded) {
        $_SESSION['download_progress'] = [
            'total' => $download_size,
            'current' => $downloaded
        ];
    }
]);
curl_exec($ch);
curl_close($ch);

前端 JavaScript:

function checkProgress() {
    fetch('progress.php')
        .then(response => response.json())
        .then(data => {
            if (data.total > 0) {
                const progress = Math.round((data.current / data.total) * 100);
                document.getElementById('progress').innerText = `${progress}%`;
            }
            if (data.current < data.total) {
                setTimeout(checkProgress, 500);
            }
        });
}

// 开始下载
fetch('download.php').then(() => checkProgress());

进度检查接口 (progress.php):

php如何实现下载进度

session_start();
header('Content-Type: application/json');
echo json_encode($_SESSION['download_progress'] ?? ['total' => 0, 'current' => 0]);

分享给朋友:

相关文章

vue如何实现重新实现主题

vue如何实现重新实现主题

Vue 主题切换的实现方法 使用 CSS 变量动态切换主题 定义主题相关的 CSS 变量,通过修改这些变量实现主题切换。在根元素(如 :root)中定义默认主题的变量,在特定类名下定义其他主题的变量。…

react如何实现插槽

react如何实现插槽

React 实现插槽的方法 React 本身没有直接提供类似 Vue 的插槽(slot)概念,但可以通过以下几种方式实现类似功能: 使用 props.children React 组件可以通过 pr…

vue如何实现滚动

vue如何实现滚动

Vue 实现滚动的方法 使用原生滚动 在Vue中可以直接使用HTML原生滚动,通过CSS设置overflow: auto或overflow: scroll来实现滚动效果。适用于简单场景。 <d…

vue如何实现ifream

vue如何实现ifream

Vue 中实现 iframe 的方法 在 Vue 中可以通过多种方式实现 iframe 的嵌入和控制。以下是几种常见的方法: 使用 HTML 原生 iframe 标签 在 Vue 模板中直接使用…

react如何实现keepalive

react如何实现keepalive

React 实现 KeepAlive 的方法 React 本身没有内置的 KeepAlive 组件,但可以通过以下方式模拟类似功能,实现组件状态保持或避免重复渲染。 使用 CSS 隐藏组件 通过 C…

react如何实现菜单

react如何实现菜单

使用 React 实现菜单的方法 使用状态管理控制菜单显示/隐藏 通过 useState 管理菜单的展开和收起状态,结合点击事件切换状态。 import { useState } from 'rea…