解决PHP超大文件下载,断点续传下载的方法详解

在网站开发中,经常需要下载一些较大的文件,比如视频、音频、PDF等。但这些文件往往都很大,如果采用普通的下载方式,可能会因为网络不稳定或者其他原因导致下载失败。为了解决这个问题,我们需要实现断点续传下载,以确保下载成功率

解决PHP超大文件下载,断点续传下载的方法详解

问题

在网站开发中,经常需要下载一些较大的文件,比如视频、音频、PDF等。但这些文件往往都很大,如果采用普通的下载方式,可能会因为网络不稳定或者其他原因导致下载失败。为了解决这个问题,我们需要实现断点续传下载,以确保下载成功率。

方法

为了实现断点续传下载,我们需要在服务器端和客户端都进行相应的处理。

服务器端处理

首先,我们需要在服务器端开启输出缓存:

ob_start();

然后,我们需要获取待下载文件的长度以及其所在路径等信息,并将其保存在一个关联数组中:

$file = $_GET['file'];
$file_size = filesize($file);
$fp = fopen($file, 'rb');
$file_info = array(
    'file_size' => $file_size,
    'file_path' => $file,
    'file_name' => basename($file),
);

接下来,我们需要判断客户端是否支持断点续传:

if (isset($_SERVER['HTTP_RANGE'])) {
    // 支持断点续传
} else {
    // 不支持断点续传
}

如果支持断点续传,就需要计算出客户端请求的部分文件的起始位置和结束位置:

$range = $_SERVER['HTTP_RANGE'];
$range = str_replace('bytes=', '', $range);
$range_arr = explode('-', $range);
$start_pos = intval($range_arr[0]);
$end_pos = $file_size - 1;
if (isset($range_arr[1]) && !empty($range_arr[1])) {
    $end_pos = intval($range_arr[1]);
}

然后,服务器就需要发送206状态码以及文件的Content-Type、Content-Disposition和Content-Range头:

header('HTTP/1.1 206 Partial Content');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' . $file_info['file_name']);
header('Content-Length: ' . ($end_pos - $start_pos + 1));
header('Content-Range: bytes ' . $start_pos . '-' . $end_pos . '/' . $file_size);

最后,服务器就可以将部分文件内容发送给客户端:

fseek($fp, $start_pos);
while (!feof($fp) && ftell($fp) <= $end_pos) {
    echo fread($fp, 1024 * 8);
    ob_flush();
    flush();
}

客户端处理

首先,我们需要发送PHP文件的请求,以获取待下载文件的信息:

$file = 'test.mp4';
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://example.com/download.php?file=' . $file);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_NOBODY, true);
$response = curl_exec($ch);
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
curl_close($ch);

然后,客户端需要判断服务器是否支持断点续传,并计算出下载文件的长度和范围:

if (preg_match('/HTTP\/1.[01] 206 Partial Content/i', $response)) {
    // 支持断点续传
    preg_match('/Content-Length: (\d+)/i', $response, $matches);
    $file_size = intval($matches[1]);
    preg_match('/Content-Range: bytes (\d+)-(\d+)\/(\d+)/i', $response, $matches);
    $start_pos = intval($matches[1]);
    $end_pos = intval($matches[2]);
} else {
    // 不支持断点续传
    preg_match('/Content-Length: (\d+)/i', $response, $matches);
    $file_size = intval($matches[1]);
    $start_pos = 0;
    $end_pos = $file_size - 1;
}

然后,客户端就需要发送请求,并将下载的文件保存在本地:

$fp = fopen('test.mp4', 'wb');
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, 'http://example.com/download.php?file=' . $file);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
curl_setopt($ch, CURLOPT_RANGE, $start_pos . '-' . $end_pos);
$response = curl_exec($ch);
fwrite($fp, $response);
fclose($fp);
curl_close($ch);

示例说明

示例1

假设我们要下载一个大小为100MB的视频文件,服务器上的路径为/var/www/example.com/video/test.mp4。客户端发送请求:

http://example.com/download.php?file=/var/www/example.com/video/test.mp4

服务器收到请求,对请求进行解析和处理,并将部分视频内容发送给客户端。客户端收到响应,并保存下载的视频文件在本地。如果下载过程中断开了连接,客户端可以再次发送请求,下载文件的同时不必重新下载之前已经下载的部分内容,以提高下载成功率。

示例2

假设我们要下载一个大小为10MB的PDF文件,服务器上的路径为/var/www/example.com/pdf/test.pdf。客户端发送请求:

http://example.com/download.php?file=/var/www/example.com/pdf/test.pdf

服务器收到请求,对请求进行解析和处理,并将整个10MB的PDF文件发送给客户端。客户端收到响应,并保存下载的PDF文件在本地。如果下载过程中断开了连接,客户端需要重新向服务器发送请求,重新下载整个文件。

本文标题为:解决PHP超大文件下载,断点续传下载的方法详解

基础教程推荐