需要讓使用者從伺服器目錄中選擇檔案?PHP 的 DirectoryIterator (SPL) 提供物件導向、記憶體友善的目錄遍歷方式,輕鬆產生 <select> 選單。本文示範完整實作,並說明過濾、排序、安全性等實務細節。
<?php
$targetDir = __DIR__ . '/uploads'; // 絕對路徑更安全
if (!is_dir($targetDir)) {
die("目錄不存在: $targetDir");
}
echo '<select name="file">';
echo '<option value="">-- 請選擇檔案 --</option>';
$iterator = new DirectoryIterator($targetDir);
foreach ($iterator as $fileInfo) {
// 跳過 . 、.. 、隱藏檔、目錄
if ($fileInfo->isDot() || $fileInfo->isDir() || $fileInfo->getFilename()[0] === '.') {
continue;
}
$filename = $fileInfo->getFilename();
// htmlentities 防 XSS,value 用原始檔名
echo '<option value="' . htmlentities($filename, ENT_QUOTES, 'UTF-8') . '">'
. htmlentities($filename, ENT_QUOTES, 'UTF-8')
. '</option>';
}
echo '</select>';
?>
<?php
function buildFileSelect(string $dir, string $name = 'file', array $options = []): string
{
$defaults = [
'recursive' => false, // 是否遞歸子目錄
'show_dirs' => false, // 是否包含目錄
'extensions' => null, // 允許副檔名陣列 ['php','txt'],null=全部
'sort_by' => 'name', // name|size|time
'sort_order' => 'asc', // asc|desc
'include_path' => false, // value 是否包含相對路徑
'placeholder' => '-- 請選擇 --',
];
$opt = array_merge($defaults, $options);
if (!is_dir($dir)) return '<!-- 目錄不存在 -->';
$iterator = $opt['recursive']
? new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS))
: new DirectoryIterator($dir);
$files = [];
foreach ($iterator as $info) {
if (!$info->isFile() && !$opt['show_dirs']) continue;
if ($info->isDir() && !$opt['show_dirs']) continue;
$filename = $info->getFilename();
if ($opt['extensions'] && !in_array(strtolower($info->getExtension()), $opt['extensions'])) continue;
$relPath = $opt['include_path']
? $info->getPathname()
: $filename;
$files[] = [
'name' => $filename,
'path' => $relPath,
'size' => $info->getSize(),
'mtime' => $info->getMTime(),
];
}
// 排序
$sortKey = $opt['sort_by'];
$order = $opt['sort_order'] === 'desc' ? -1 : 1;
usort($files, fn($a, $b) => $order * strcmp($a[$sortKey], $b[$sortKey]));
// 產生 HTML
$html = '<select name="' . htmlentities($name) . '">';
if ($opt['placeholder']) {
$html .= '<option value="">' . htmlentities($opt['placeholder']) . '</option>';
}
foreach ($files as $f) {
$html .= '<option value="' . htmlentities($f['path']) . '">'
. htmlentities($f['name']) . '</option>';
}
$html .= '</select>';
return $html;
}
// 使用範例
echo buildFileSelect(__DIR__ . '/templates', 'template', [
'recursive' => true,
'extensions' => ['php', 'html', 'twig'],
'sort_by' => 'mtime',
'sort_order' => 'desc',
'include_path'=> true,
]);
?>
| 方法 | 說明 |
|---|---|
getFilename() | 取得檔名(含副檔名) |
getBasename($suffix) | 取得檔名,可選移除副檔名 |
getExtension() | 取得副檔名(不含點) |
getPathname() | 取得完整路徑 |
getPath() | 取得所在目錄路徑 |
isFile() / isDir() / isLink() | 類型判斷 |
isDot() | 是否為 . 或 .. |
isReadable() / isWritable() | 權限檢查 |
getSize() | 檔案大小(位元組) |
getMTime() / getATime() / getCTime() | 修改/存取/建立時間(Unix 時間戳) |
getPerms() | 權限位元遮罩 |
realpath() 驗證結果仍在基礎目錄下(防止目錄遍歷攻擊 ../../etc/passwd)。htmlentities() 防 XSS。DirectoryIterator 逐一載入,記憶體穩定,適合大量檔案目錄。getPathname() 等方法,若需多次使用請先存變數。掌握 DirectoryIterator,不只能做下拉選單,更是建構檔案管理器、圖片庫、程式碼編輯器等功能的基石。