PHP实现文件下载断点续传功能详解

程一诺
程一诺
发布时间:2026-10-14| 2人浏览过
原创

在Web开发中,大文件下载若因网络波动中断,用户需重新下载十分不便。通过PHP处理HTTP Range请求头并返回206 Partial Content状态,可实现高效的断点续传功能。本文将解析其核心原理并提供可直接使用的PHP代码实现。

断点续传的实现原理

断点续传的核心在于客户端与服务器对HTTP协议中特定字段的交互:

  1. 客户端发起请求:若之前已下载部分数据,客户端会在请求头中包含 Range: bytes=xxxx- 字段,指示期望从哪个字节位置开始接收。
  2. 服务器响应:PHP脚本检测到该头部后,不发送完整的200 OK,而是设置 HTTP/1.1 206 Partial Content 状态码。
  3. 数据分段传输:配合 Content-Range 头(格式为 bytes start-end/total)和正确的 Content-Length,告知浏览器当前传输的范围及总大小。

PHP 断点续传代码实现

以下代码演示了如何构建一个支持断点续传的下载函数 dl_file_resume。该函数会检测文件、确定MIME类型,并根据是否有Range头分别处理完整下载和断点续传逻辑。

第1步:初始化环境与参数检查

首先定义入口函数,接收文件路径作为参数。验证文件是否存在,获取文件大小、文件名及扩展名,用于后续的内容类型判断。

function dl_file_resume($file) {
    // 1. 检测文件是否存在
    if (!is_file($file)) { die("<b>404 File not found!</b>"); }
    
    $len = filesize($file);      // 获取文件大小 (Total Size)
    $filename = basename($file);  // 获取文件名
    // 获取文件扩展名并转为小写
    $file_extension = strtolower(substr(strrchr($filename, "."), 1));
}

第2步:设置 MIME 内容类型

根据文件扩展名设置对应的 Content-Type,以确保浏览器能正确识别文件格式。对于未知类型,默认使用通用的强制下载类型。

    // 2. 根据扩展名指定输出浏览器格式
    switch ($file_extension) {
        case "exe":   $ctype = "application/octet-stream"; break;
        case "zip":   $ctype = "application/zip"; break;
        case "mp3":   $ctype = "audio/mpeg"; break;
        case "mpg":   $ctype = "video/mpeg"; break;
        case "avi":   $ctype = "video/x-msvideo"; break;
        default:      $ctype = "application/force-download";
    }

第3步:处理 HTTP 请求头(核心逻辑)

此步骤区分“首次下载”和“断点续传”。关键在于解析 $_SERVER['HTTP_RANGE']。
注意:原代码中 `str_replace` 用法存在逻辑瑕疵,在实际应用中应直接解析字符串结构。下方展示了修正后的健壮性写法:

    // 3. 缓存控制与基础 Header
    header("Cache-Control: public");
    header("Content-Type: $ctype");
    
    // 处理 IE 浏览器文件名编码问题
    if (strstr($_SERVER['HTTP_USER_AGENT'], "MSIE")) {
        $iefilename = preg_replace('/\./', '%2e', $filename, substr_count($filename, '.') - 1);
        header("Content-Disposition: attachment; filename=\"$iefilename\"");
    } else {
        header("Content-Disposition: attachment; filename=\"$filename\"");
    }
    header("Accept-Ranges: bytes");

    $size = filesize($file);
    
    // 4. 判断是否为断点续传请求
    if (isset($_SERVER['HTTP_RANGE'])) {
        // --- 断点续传模式 (Partial Content) ---
        // 解析格式如: bytes=4390912-
        list($a, $range) = explode("=", $_SERVER['HTTP_RANGE']);
        
        // 清理 range 字符串,去除可能的非数字字符或空格
        $range = trim($range, ' -');
        
        // 如果 range 为空或无效,通常意味着从最后开始或全量,这里简化处理
        if ($range === '' || !is_numeric($range)) {
             // 简单的 fallback,实际项目建议更严格的校验
             $start_pos = 0;
             $range_str = "0-";
        } else {
            $start_pos = intval($range);
            $range_str = $range . "-";
        }

        $end_pos = $size - 1;          // 文件最后一个字节索引
        $new_length = $end_pos - $start_pos + 1; // 待下载的长度
        
        // 发送 206 状态码及相关头信息
        header("HTTP/1.1 206 Partial Content");
        header("Content-Length: $new_length");
        header("Content-Range: bytes $start_pos-$end_pos/$size");
    } else {
        // --- 首次完整下载模式 ---
        $end_pos = $size - 1;
        header("Content-Range: bytes 0-$end_pos/$size");
        header("Content-Length: $size");
    }

第4步:读取并输出文件流

使用 fopen 以二进制只读模式打开文件,利用 fseek 定位到起始字节,然后通过循环读取并输出,同时刷新缓冲区以保证实时传输效果。

    // 5. 打开文件并定位指针
    $fp = fopen($file, "rb");
    
    // 如果有断点续传,跳过已下载部分
    $start_offset = isset($_SERVER['HTTP_RANGE']) ? 
                    (list($a, $r)=explode('=', $_SERVER['HTTP_RANGE']), intval(trim($r))) : 0;
    
    if ($fp) {
        fseek($fp, $start_offset);
        
        // 关闭执行时间限制,防止大文件下载超时
        set_time_limit(0);
        
        // 循环输出数据块
        while(!feof($fp)) {
            print(fread($fp, 1024 * 8)); // 每次输出 8KB
            flush();       // 刷新 PHP 缓冲
            ob_flush();    // 刷新 Web Server 缓冲
        }
        fclose($fp);
    }
    exit;
}

使用注意事项

  • 兼容性:大多数现代浏览器和下载工具(如 Thunderbird, IDM)均支持 Range 头。但对于某些旧式或非标准客户端,可能仍需降级处理。
  • 安全性:在生产环境中,务必对传入的 $file 进行严格的路径校验,防止目录遍历攻击(Path Traversal),例如过滤掉 .. 等字符。
  • 性能:对于超大文件(GB级别),直接使用PHP逐字节读取可能占用较多内存和时间。可考虑结合操作系统命令如 system("cat \"$file\" | tail -c +$offset") 或直接配置 Nginx/Apache 处理静态文件下载,PHP 仅做权限验证。

以上就是PHP实现文件下载断点续传的详细内容,更多关于PHP断点续传的资料请关注本站其它相关文章!

相关标签:

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。