在 Linux 上使用 PHP 实现文件下载,可以通过多种方法来完成。以下介绍几种常用的方法,并附有示例代码,帮助你轻松实现文件下载功能。
readfile() 函数readfile() 是一个简单且高效的函数,适用于直接将文件内容发送到浏览器进行下载。
<?php
// 设置要下载的文件路径
$file = 'path/to/your/file.zip';
// 检查文件是否存在
if (file_exists($file)) {
// 设置下载时的头信息
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');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
// 清空输出缓冲区
flush();
// 读取文件并输出到浏览器
readfile($file);
exit;
} else {
echo "文件不存在。";
}
?>
'path/to/your/file.zip' 替换为你希望用户下载的文件的实际路径。file_exists() 函数确认文件是否存在,以避免错误。Content-Type: 设置为 application/octet-stream 适用于大多数文件类型,浏览器会提示下载。Content-Disposition: 设置为 attachment 并指定文件名,使浏览器以下载方式处理文件。Expires, Cache-Control, Pragma 等用于控制缓存行为。readfile() 函数将文件内容直接输出到浏览器。fpassthru() 函数fpassthru() 函数适用于大文件下载,因为它直接将文件指针传递给浏览器,不需要将整个文件加载到内存中。
<?php
$file = 'path/to/your/largefile.iso';
if (file_exists($file)) {
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
// 清空输出缓冲区
flush();
// 将文件指针传递给浏览器
fpassthru(fopen($file, 'rb'));
exit;
} else {
echo "文件不存在。";
}
?>
与 readfile() 类似,但 fpassthru() 更适合处理大文件,因为它避免了将整个文件内容加载到 PHP 内存中。
对于非常大的文件,或者需要在传输过程中进行一些处理的情况,可以使用输出缓冲区分块读取文件。
<?php
$file = 'path/to/your/largevideo.mp4';
if (file_exists($file)) {
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
// 清空输出缓冲区
flush();
// 打开文件
$handle = fopen($file, 'rb');
if ($handle === false) {
echo "无法打开文件。";
exit;
}
// 分块读取并输出文件
while (!feof($handle)) {
echo fread($handle, 8192);
flush();
if (connection_status() != 0) {
fclose($handle);
exit;
}
}
fclose($handle);
exit;
} else {
echo "文件不存在。";
}
?>
fread() 函数每次读取固定大小的数据块(如 8KB),然后输出到浏览器。connection_status() 检查是否有中断,适合处理大文件下载。stream_copy_to_stream() 函数(PHP 8.0+)stream_copy_to_stream() 提供了一种高效的方式来将一个流复制到另一个流,适用于需要更复杂处理的场景。
<?php
$file = 'path/to/your/document.pdf';
if (file_exists($file)) {
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
// 清空输出缓冲区
flush();
// 打开文件流和输出流
$fileStream = fopen($file, 'rb');
$outputStream = fopen('php://output', 'wb');
if ($fileStream === false || $outputStream === false) {
echo "无法打开文件或输出流。";
exit;
}
// 复制文件流到输出流
stream_copy_to_stream($fileStream, $outputStream);
// 关闭流
fclose($fileStream);
fclose($outputStream);
exit;
} else {
echo "文件不存在。";
}
?>
fopen() 分别打开要下载的文件和 PHP 的输出流 php://output。stream_copy_to_stream() 将文件内容直接复制到输出流。权限设置:确保 PHP 进程对要下载的文件具有读取权限。通常,文件权限应设置为 644 或类似权限,目录权限应允许 PHP 脚本访问。
chmod 644 /path/to/your/file.zip
安全考虑:
basename() 函数获取文件名,并避免直接拼接用户输入。$file = 'path/to/your/files/' . basename($_GET['file']);
处理大文件:对于非常大的文件,建议使用 fpassthru() 或分块读取的方法,以避免消耗过多内存。
设置合适的头信息:根据文件类型设置正确的 Content-Type,有助于浏览器正确处理和显示文件。如果不确定文件类型,可以使用通用的 application/octet-stream。
错误处理:在实际应用中,添加更多的错误处理和日志记录,以便于调试和维护。
以下是一个综合了上述注意事项的完整示例:
<?php
// 下载文件的函数
function downloadFile($filepath) {
// 基础路径,防止目录遍历
$baseDir = '/path/to/your/files/';
// 获取文件名,防止路径信息泄露
$filename = basename($filepath);
// 完整路径
$fullPath = $baseDir . $filename;
// 检查文件是否存在
if (file_exists($fullPath)) {
// 检查用户是否有权限访问
if (!is_readable($fullPath)) {
echo "没有权限读取该文件。";
exit;
}
// 设置下载头信息
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . htmlspecialchars($filename) . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($fullPath));
// 清空输出缓冲区
flush();
// 根据 PHP 版本选择合适的函数
if (function_exists('fpassthru')) {
fpassthru(fopen($fullPath, 'rb'));
} else {
// 分块读取文件
$chunkSize = 1024 * 1024; // 1MB
$handle = fopen($fullPath, 'rb');
if ($handle === false) {
echo "无法打开文件。";
exit;
}
while (!feof($handle)) {
echo fread($handle, $chunkSize);
flush();
if (connection_status() != 0) {
fclose($handle);
exit;
}
}
fclose($handle);
}
exit;
} else {
echo "文件不存在。";
}
}
// 调用下载函数,例如通过 GET 参数传递文件名
if (isset($_GET['file'])) {
downloadFile($_GET['file']);
} else {
echo "请指定要下载的文件。";
}
?>
download.php。$baseDir 设置为存放可下载文件的目录,并且该目录对 PHP 进程具有读取权限。download.php?file=example.zip 来下载 example.zip 文件。以上介绍了在 Linux 上使用 PHP 实现文件下载的几种常用方法,包括 readfile()、fpassthru()、分块读取以及 stream_copy_to_stream()。根据具体需求和文件大小选择合适的方法,并注意安全性和性能优化。希望这些示例对你有所帮助!