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

如何用PHP从远程服务器直接流式传输MP3、MP4等媒体文件?

实现PHP跨服务器流式传输媒体文件的正确姿势

你遇到的问题本质是全量下载后转发导致的延迟和资源浪费,正确的做法是让服务器A作为「代理」,把服务器B的文件分段流式传递给客户端,不用把整个文件存在A上。下面是具体的实现方案:

核心思路

服务器A向服务器B发起请求,同时把收到的文件数据立刻转发给客户端,全程保持数据流的开放,不缓存完整文件。还要支持浏览器的Range请求(媒体播放时的断点续传、进度拖动都靠这个)。

完整代码示例

<?php
// 1. 安全验证:这里可以加你的权限逻辑,比如token校验、IP白名单等
$allowed = false;
// 示例:假设通过GET参数传递验证token
if (isset($_GET['token']) && $_GET['token'] === 'your_secure_token') {
    $allowed = true;
}
if (!$allowed) {
    http_response_code(403);
    exit('Forbidden');
}

// 2. 目标文件在服务器B的地址(可以是内部IP/域名,确保A能访问B)
$remoteFileUrl = 'http://server-b.example.com/media/your-file.mp3';

// 3. 先获取远程文件的元信息(大小、类型),支持Range请求
$ch = curl_init($remoteFileUrl);
curl_setopt($ch, CURLOPT_NOBODY, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
$headers = curl_exec($ch);
curl_close($ch);

// 解析头部信息
$contentLength = 0;
$contentType = 'application/octet-stream';
foreach (explode("\r\n", $headers) as $header) {
    if (strpos($header, 'Content-Length:') === 0) {
        $contentLength = (int)trim(substr($header, 15));
    } elseif (strpos($header, 'Content-Type:') === 0) {
        $contentType = trim(substr($header, 13));
    }
}

// 4. 处理客户端的Range请求(媒体播放必备)
$range = '';
if (isset($_SERVER['HTTP_RANGE'])) {
    $range = $_SERVER['HTTP_RANGE'];
    // 解析Range,比如 "bytes=0-1023"
    preg_match('/bytes=(\d+)-(\d+)?/', $range, $matches);
    $start = (int)$matches[1];
    $end = isset($matches[2]) ? (int)$matches[2] : $contentLength - 1;
    $length = $end - $start + 1;

    // 返回206 Partial Content
    http_response_code(206);
    header("Content-Range: bytes $start-$end/$contentLength");
    header("Content-Length: $length");
} else {
    // 正常请求返回200
    http_response_code(200);
    header("Content-Length: $contentLength");
}

// 5. 设置必要的响应头部,让浏览器识别为媒体文件
header("Content-Type: $contentType");
header("Accept-Ranges: bytes");
header("Cache-Control: public, max-age=31536000"); // 可选:设置缓存时间
header("Content-Disposition: inline; filename=\"" . basename($remoteFileUrl) . "\""); // 让浏览器直接播放,不是下载

// 6. 流式转发远程文件
$ch = curl_init($remoteFileUrl);
if (!empty($range)) {
    curl_setopt($ch, CURLOPT_RANGE, $range);
}
curl_setopt($ch, CURLOPT_RETURNTRANSFER, false);
curl_setopt($ch, CURLOPT_WRITEFUNCTION, function($ch, $data) {
    echo $data;
    ob_flush();
    flush();
    return strlen($data);
});
curl_exec($ch);
curl_close($ch);
exit;
?>

关键细节说明

  • 安全验证:一定要加!比如token校验、用户权限检查,防止别人通过你的PHP脚本随意访问服务器B的文件。
  • Range请求支持:没有这个的话,用户拖动媒体进度条会失效,浏览器会重新下载整个文件。
  • 流式传输:用CURLOPT_WRITEFUNCTION实时把收到的字节输出给客户端,配合ob_flush()和flush()确保数据立刻发送,不会被PHP缓存。
  • 服务器B的访问权限:确保服务器A可以访问服务器B的文件(比如内部网络打通,或者B的防火墙允许A的IP访问),如果B需要认证,可以在cURL里加CURLOPT_USERPWD或者其他认证方式。
  • 避免内存溢出:这种方式不会把整个文件加载到PHP内存里,所以不用担心大文件导致的内存不足问题,比file_get_contents更可靠。

替代方案:用Apache/Nginx反向代理

如果你的服务器用的是Apache或Nginx,其实可以直接用反向代理来实现,性能比PHP更好:

  • Nginx示例:在A的配置里加一个location,把请求转发到B,同时支持Range请求:
location /media/ {
    proxy_pass http://server-b.example.com/media/;
    proxy_set_header Range $http_range;
    proxy_set_header If-Range $http_if_range;
    proxy_redirect off;
    proxy_buffering off; # 禁用缓存,实现流式传输
}

这种方式性能更高,不用PHP处理,适合高并发场景,但需要你有服务器配置权限。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.21 03:55:54