
本文介绍在php中安全列出指定磁盘(如d:\)下的所有文件(自动过滤子目录),并实现点击下载pdf等文件的功能,同时规避直接暴露本地路径的安全风险。
本文介绍在php中安全列出指定磁盘(如d:\)下的所有文件(自动过滤子目录),并实现点击下载pdf等文件的功能,同时规避直接暴露本地路径的安全风险。
在Web开发中,直接通过PHP读取并提供本地物理驱动器(如D:\)中的文件下载存在严重安全隐患——例如路径遍历、任意文件读取、权限越界等问题。因此,不推荐使用 scandir('D:/') 并直接生成 <a href="D:/xxx.pdf"></a> 链接的方式(浏览器无法解析本地Windows路径,且服务器端未做权限校验极易被滥用)。
✅ 正确做法是采用间接映射 + 服务端受控下载模式:
-
预定义可信文件目录(如
D:\uploads\),仅扫描该目录下的一级文件; -
过滤掉文件夹,只保留普通文件(可通过
is_file()判断); - 不暴露真实路径,而是为每个文件分配唯一ID或安全别名;
- 所有下载请求统一经由
download.php处理,执行MIME类型检测与流式输出。
以下是完整、安全的实现示例:
✅ 步骤1:安全列出D盘指定目录下的文件(排除文件夹)
<?php
// 定义可信根目录(需确保Web服务器有读取权限,且不在Web可直接访问路径下)
$baseDir = 'D:/shared_documents/'; // 注意:结尾斜杠必须存在
if (!is_dir($baseDir)) {
die('指定目录不存在或不可访问');
}
$files = array_filter(scandir($baseDir), function($item) use ($baseDir) {
return is_file($baseDir . $item); // 严格过滤,仅保留文件
});
// 移除 . 和 ..(尽管 is_file 已过滤,此处双重保险)
$files = array_diff($files, ['.', '..']);
?>
<h3>可下载文件列表:</h3>
<ul>
<?php foreach ($files as $filename): ?>
<?php
$safeId = urlencode(base64_encode($filename)); // 简单ID化,避免特殊字符
// 或更推荐:查数据库获取自增ID(见下方说明)
?>
<li><a href="download.php?file=<?= $safeId ?>" download="<?= htmlspecialchars($filename) ?>">
<?= htmlspecialchars($filename) ?>
</a></li>
<?php endforeach; ?>
</ul>✅ 步骤2:download.php —— 安全下载入口(关键!)
<?php
// download.php —— 所有下载必须经由此脚本
if (!isset($_GET['file'])) {
http_response_code(400);
die('Missing file parameter');
}
// 白名单校验 & 路径安全化(防止 ../ 绕过)
$encoded = $_GET['file'];
$filename = base64_decode(urldecode($encoded));
// 严格限制文件名格式(仅允许字母、数字、下划线、短横线、点号)
if (!preg_match('/^[a-zA-Z0-9._\-]+$/', $filename)) {
http_response_code(403);
die('Invalid filename');
}
$baseDir = 'D:/shared_documents/';
$filePath = $baseDir . $filename;
// 再次验证:是否为真实文件、是否在白名单目录内
if (!is_file($filePath) || realpath($filePath) !== realpath($filePath)) {
http_response_code(404);
die('File not found or access denied');
}
// 设置响应头,触发下载(非直接打开)
header('Content-Type: ' . (mime_content_type($filePath) ?: 'application/octet-stream'));
header('Content-Disposition: attachment; filename="' . basename($filename) . '"');
header('Content-Length: ' . filesize($filePath));
header('Cache-Control: no-cache');
readfile($filePath);
exit;⚠️ 重要注意事项:
-
禁止将
D:/根目录或用户桌面等敏感路径设为$baseDir;应单独创建一个专用共享目录(如D:/web_downloads/),并配置NTFS权限仅允许IIS/Apache用户读取。 - 生产环境强烈建议改用数据库映射方案:将文件名、真实路径、MIME类型、状态等存入MySQL表,通过
id查询而非明文文件名,彻底杜绝路径注入风险。 -
download.php必须校验Content-Type,对.php、.htaccess等危险扩展名做拦截(可添加黑名单)。 - 若需支持大文件,请启用
ob_flush()+flush()分块输出,并调整max_execution_time和output_buffering。
通过以上设计,既满足了“浏览D盘文件并下载”的业务需求,又符合Web安全最佳实践——永远不要让客户端决定要读哪个物理路径,而应由服务端严格管控每一次文件访问。


















