本文目录导读:

在PHP中实现文件下载功能有多种方法,下面我介绍几种常见的实现方式。
基本文件下载(直接链接)
最简单的下载方式,文件放在服务器可访问的目录:
// 文件在服务器上的路径
$file = 'path/to/your/file.pdf';
$filename = 'download.pdf'; // 下载时显示的文件名
if (file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
readfile($file);
exit;
}
通过URL参数下载(常见方式)
<?php
// download.php?id=123
$fileId = $_GET['id'];
// 从数据库获取文件信息
$fileInfo = getFileInfoFromDatabase($fileId); // 自定义函数
$file = 'uploads/' . $fileInfo['stored_name'];
$filename = $fileInfo['original_name'];
if (file_exists($file)) {
// 设置下载头
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
// 清空输出缓冲
ob_clean();
flush();
// 输出文件内容
readfile($file);
exit;
} else {
echo '文件不存在';
}
?>
大文件下载(分块传输)
对于大文件,使用分块传输避免内存溢出:
function downloadFile($file, $filename = null) {
if (!file_exists($file)) {
return false;
}
if (!$filename) {
$filename = basename($file);
}
// 获取文件大小
$fileSize = filesize($file);
// 设置下载头
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . $fileSize);
// 清空输出缓冲
ob_clean();
flush();
// 打开文件
$handle = fopen($file, 'rb');
// 分块读取并输出
$chunkSize = 1024 * 1024; // 1MB
while (!feof($handle)) {
echo fread($handle, $chunkSize);
ob_flush();
flush();
}
fclose($handle);
exit;
}
// 使用示例
downloadFile('large_file.mp4', '我的视频.mp4');
断点续传下载
function resumeDownload($file, $filename = null) {
if (!file_exists($file)) {
return false;
}
if (!$filename) {
$filename = basename($file);
}
$fileSize = filesize($file);
$start = 0;
$end = $fileSize - 1;
// 处理断点续传
if (isset($_SERVER['HTTP_RANGE'])) {
$range = $_SERVER['HTTP_RANGE'];
$range = str_replace('bytes=', '', $range);
$range = explode('-', $range);
if (isset($range[0]) && $range[0] !== '') {
$start = intval($range[0]);
}
if (isset($range[1]) && $range[1] !== '') {
$end = intval($range[1]);
}
header('HTTP/1.1 206 Partial Content');
header('Content-Range: bytes ' . $start . '-' . $end . '/' . $fileSize);
} else {
header('HTTP/1.1 200 OK');
}
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Length: ' . ($end - $start + 1));
$handle = fopen($file, 'rb');
fseek($handle, $start);
$chunkSize = 1024 * 1024;
$remaining = ($end - $start + 1);
while ($remaining > 0) {
$readSize = min($chunkSize, $remaining);
echo fread($handle, $readSize);
$remaining -= $readSize;
ob_flush();
flush();
}
fclose($handle);
exit;
}
下载不同类型的文件
根据文件类型设置正确的MIME类型:
function getMimeType($file) {
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $file);
finfo_close($finfo);
return $mimeType;
}
function downloadWithCorrectType($file, $filename = null) {
if (!file_exists($file)) {
return false;
}
if (!$filename) {
$filename = basename($file);
}
$mimeType = getMimeType($file);
header('Content-Description: File Transfer');
header('Content-Type: ' . $mimeType);
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
exit;
}
HTML下载按钮示例
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>文件下载</title>
</head>
<body>
<h2>文件下载</h2>
<ul>
<li>
<a href="download.php?id=1">下载文档1.pdf</a>
</li>
<li>
<a href="download.php?id=2">下载图片2.jpg</a>
</li>
<li>
<form action="download.php" method="post">
<input type="hidden" name="file_id" value="3">
<button type="submit">下载视频3.mp4</button>
</form>
</li>
</ul>
</body>
</html>
重要注意事项
- 安全性:始终验证用户是否有权限下载文件
- 路径安全:防止目录遍历攻击
- 文件验证:确保文件存在且可读
- 大文件处理:使用分块传输处理大文件
- 错误处理:添加适当的错误处理机制
// 安全验证示例
function validateDownload($fileId, $userId) {
// 验证用户权限
if (!hasPermission($userId, 'download')) {
return false;
}
// 验证文件属于该用户
$file = getFileById($fileId);
if ($file['user_id'] !== $userId) {
return false;
}
return true;
}
// 防止目录遍历
function sanitizeFilename($filename) {
$filename = basename($filename);
$filename = str_replace(['../', '..\\', './', '.\\'], '', $filename);
$filename = preg_replace('/[^\w\-_\. ]/', '', $filename);
return $filename;
}
选择合适的下载方式取决于你的具体需求:小文件用简单方法,大文件用分块传输,需要续传功能用断点续传实现。