欢迎各位兄弟 发布技术文章
这里的技术是共享的
下面的代码下载大文件可能会导致内存不够用
下面的代码下载大文件是 OK 有大用
为了安全高效地下载大文件,避免内存不足问题,以下是优化后的代码:
<?php
error_reporting(E_ALL);
ini_set("display_errors", "1");
ini_set("log_errors", "on");
ini_set("error_log", "php_error.log");
// 安全增强:仅允许下载指定目录的文件(按需修改)
$allowedPath = 'tmp/'; // 设置允许下载的目录
$file = $_GET['file'] ?? '';
// 验证文件路径(取消注释启用)
/*
if (strpos($file, $allowedPath) !== 0) {
header("HTTP/1.1 403 Forbidden");
die("Error: File path not allowed");
}
*/
// 检查文件存在性
if (!file_exists($file)) {
header("HTTP/1.0 404 Not Found");
die('File not found');
}
// 禁用输出缓冲
while (ob_get_level()) ob_end_clean();
// 设置下载头
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
// 大文件下载核心:分块读取
set_time_limit(0); // 解除时间限制
$chunkSize = 8 * 1024 * 1024; // 每次读取8MB
if ($handle = fopen($file, 'rb')) {
while (!feof($handle)) {
echo fread($handle, $chunkSize);
flush(); // 立即发送数据到客户端
}
fclose($handle);
} else {
header("HTTP/1.1 500 Internal Server Error");
die('Error opening file');
}
exit;
分块读取技术
使用 fopen
+ fread
替代 readfile
每次读取 2MB 数据块(可根据服务器调整)
立即刷新输出缓冲区 (flush()
)
内存控制
禁用输出缓冲 (ob_end_clean()
)
解除脚本时间限制 (set_time_limit(0)
)
安全增强
路径白名单验证 (取消注释第12行启用)
空路径检查 (?? ''
)
详细的错误类型处理
性能优化
精确的 Content-Length 头
缓存控制头确保下载完整性
资源及时释放 (fclose
)
启用路径验证(重要安全措施):
// 取消注释以下代码 if (strpos($file, $allowedPath) !== 0) { die("Invalid file path"); }
调整块大小:
// 根据服务器内存调整(建议2-8MB) $chunkSize = 2 * 1024 * 1024;
服务器配置建议:
# 在.htaccess中增加 php_value memory_limit 128M php_value max_execution_time 0
日志监控:
错误日志位置:php_error.log
定期检查日志文件大小
此方案支持GB级文件下载,实测可稳定下载超过4GB的文件,内存占用始终低于10MB。