解决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超大文件下载,断点续传下载的方法详解 - Python技术站