You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP浏览器在线预览文件(含MP4)速度过慢问题求助

解决PHP在线预览长视频加载缓慢的问题

哥们,你的问题根源太明确了——没处理HTTP Range请求!浏览器播放长视频时,会自动发分段请求(带Range请求头),只拿当前需要的视频片段,实现“边看边加载”。但你的代码不管这套,每次都把50分钟的视频全量返回,浏览器只能硬等整个文件下载完才能播放,自然慢得离谱。而直接访问文件时,Apache自动处理了Range请求,所以速度快得多。

接下来我给你改代码,加上Range请求支持,同时区分在线预览和下载两种场景:

修改后的完整代码

<?php
// 自动引入部分(保留原有逻辑)
//Part of auto_include.php Start
$root = $_SERVER['DOCUMENT_ROOT'];
include($root.'/engine/conn_db.php');
//Part of auto_include.php End

$file_key = $_GET['file_key'];
$file_exist = false;

// 查询文件信息并验证实际存在性
if($result = $db->query("SELECT * FROM `files` WHERE `id_key`='{$file_key}'")) {
    if($row = $result->fetch_object()) {
        $file_key = $row->id_key;
        $file_name = $row->name;
        $file_size = $row->size;
        $file_mime = $row->mime;
        $file_path_rel = '/get/'.$file_key.'/'.$file_name;
        $file_path = $root.$file_path_rel;
        $file_exist = file_exists($file_path); // 实际校验文件是否存在
    }
    $result->free();
}

if(!$file_exist) {
    http_response_code(404);
    die("文件不存在");
}

// 通过URL参数区分在线预览/下载模式
$is_download = isset($_GET['download']) && $_GET['download'] == 1;

// 处理HTTP Range请求,实现分段加载
$handle = fopen($file_path, 'rb');
if(!$handle) {
    http_response_code(500);
    die("无法打开文件");
}

$actual_file_size = filesize($file_path); // 确保文件大小准确
$start = 0;
$end = $actual_file_size - 1;

// 解析Range请求头
if(isset($_SERVER['HTTP_RANGE'])) {
    if(preg_match('/bytes=(\d+)-(\d*)/', $_SERVER['HTTP_RANGE'], $matches)) {
        $start = intval($matches[1]);
        $end = !empty($matches[2]) ? intval($matches[2]) : $actual_file_size - 1;
        $end = min($end, $actual_file_size - 1); // 防止超出文件范围
        http_response_code(206); // 返回部分内容状态码
    } else {
        // 无效Range请求,返回416状态码
        http_response_code(416);
        header("Content-Range: bytes */$actual_file_size");
        fclose($handle);
        die();
    }
}

// 设置通用响应头
header('Accept-Ranges: bytes');
header('Cache-Control: max-age=0, no-store');
header('Connection: Keep-Alive');
header('Keep-Alive: timeout=10, max=100');
header('Server: Apache');

// 根据模式设置内容类型
if($is_download) {
    // 下载模式:触发浏览器保存文件
    header("Content-Type: application/octet-stream");
    header("Content-Disposition: attachment; filename=\"$file_name\"");
} else {
    // 预览模式:返回文件原生MIME类型
    header("Content-Type: $file_mime");
}

// 设置分段响应的长度和范围头
$output_length = $end - $start + 1;
header("Content-Length: $output_length");
if(isset($_SERVER['HTTP_RANGE'])) {
    header("Content-Range: bytes $start-$end/$actual_file_size");
}

// 分块输出文件,避免内存溢出
$chunk_size = 1024 * 1024; // 1MB分块
$remaining = $output_length;
fseek($handle, $start);

while($remaining > 0 && !feof($handle)) {
    $read_size = min($chunk_size, $remaining);
    echo fread($handle, $read_size);
    $remaining -= $read_size;
    // 强制刷新输出缓冲,避免卡顿
    ob_flush();
    flush();
}

fclose($handle);
?>

关键改进点说明

  • 支持Range请求:解析浏览器的分段请求,只返回当前需要的视频片段,实现边看边缓冲,和直接访问文件速度一致
  • 分块输出:用fread分块读取文件,避免一次性把大文件塞进内存,防止内存溢出
  • 区分预览/下载:通过?download=1参数切换模式,下载时不再需要等全量加载,而是直接触发浏览器的分段下载流程
  • 完善错误处理:增加文件存在性校验、文件打开失败、无效Range请求的处理,返回标准HTTP状态码
  • 准确文件大小:用filesize()获取实际文件大小,避免数据库存储值和实际文件不一致的问题

使用方式

  • 在线预览:直接访问URL,比如yourdomain.com/yourscript.php?file_key=xxx,浏览器会自动分段加载视频
  • 下载:在URL后加参数?download=1,比如yourdomain.com/yourscript.php?file_key=xxx&download=1,触发浏览器保存文件

这样修改后,长视频在线预览的速度就会和直接访问文件一样快了,浏览器能按需加载片段,不用等整个文件下载完。

内容的提问来源于stack exchange,提问作者DIES

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:45:01