opendir() 函数用于打开一个目录句柄,以便后续使用 readdir()、rewinddir() 和 closedir() 函数来读取和操作目录内容。
目录句柄是 PHP 用于访问目录内容的资源标识符。使用 opendir() 函数后,必须使用 closedir() 函数来关闭目录句柄,以避免资源泄漏。
opendir(string $directory, ?resource $context = null): resource|false
| 参数 | 描述 |
|---|---|
| directory |
必需。要打开的目录路径。 可以是绝对路径或相对路径。相对路径将相对于当前工作目录进行解析。 |
| context |
可选。流上下文(stream context)资源。 用于指定流的特殊选项,例如访问 FTP、HTTP 或其他流包装器时需要。 |
| 返回值 | 描述 |
|---|---|
| resource | 成功打开目录时返回目录句柄资源 |
| FALSE | 打开目录失败(目录不存在、没有权限或路径无效) |
<?php
// 打开目录
$directory = "/tmp";
$handle = opendir($directory);
// 检查是否成功打开
if ($handle) {
echo "成功打开目录: $directory<br>";
// 读取目录内容
echo "目录内容:<br>";
while (false !== ($entry = readdir($handle))) {
echo htmlspecialchars($entry) . "<br>";
}
// 关闭目录句柄
closedir($handle);
echo "目录句柄已关闭";
} else {
echo "无法打开目录: $directory";
}
?>
<?php
/**
* 获取目录中所有图片文件
* @param string $dir 目录路径
* @return array 图片文件列表
*/
function getImageFiles($dir) {
$images = [];
// 打开目录
if ($handle = opendir($dir)) {
// 定义图片扩展名
$imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'];
// 遍历目录内容
while (false !== ($entry = readdir($handle))) {
// 跳过 . 和 ..
if ($entry === '.' || $entry === '..') {
continue;
}
// 获取文件扩展名
$extension = strtolower(pathinfo($entry, PATHINFO_EXTENSION));
// 检查是否为图片文件
if (in_array($extension, $imageExtensions)) {
$images[] = $entry;
}
}
// 关闭目录句柄
closedir($handle);
}
return $images;
}
// 使用示例
$imageDir = "/var/www/images";
$images = getImageFiles($imageDir);
echo "在 $imageDir 中找到的图片文件:<br>";
if (count($images) > 0) {
foreach ($images as $image) {
echo "- $image<br>";
}
} else {
echo "没有找到图片文件";
}
?>
<?php
// 访问FTP目录的示例
$ftpUrl = "ftp://example.com/public_html/";
// 创建FTP上下文选项
$options = [
'ftp' => [
'username' => 'ftp_user',
'password' => 'ftp_password',
'timeout' => 30
]
];
// 创建流上下文
$context = stream_context_create($options);
// 打开FTP目录
if ($handle = @opendir($ftpUrl, $context)) {
echo "FTP目录内容:<br>";
while (false !== ($entry = readdir($handle))) {
echo $entry . "<br>";
}
closedir($handle);
} else {
echo "无法访问FTP目录,请检查连接参数";
}
// 访问ZIP文件内容(需要php_zip扩展)
$zipPath = "zip:///path/to/archive.zip#";
if ($handle = @opendir($zipPath)) {
echo "ZIP文件内容:<br>";
while (false !== ($entry = readdir($handle))) {
echo $entry . "<br>";
}
closedir($handle);
}
?>
<?php
/**
* 递归遍历目录并计算文件总数和大小
* @param string $dir 要遍历的目录
* @return array 统计信息
*/
function scanDirectoryRecursive($dir) {
$stats = [
'files' => 0,
'directories' => 0,
'size' => 0
];
// 打开目录
if ($handle = opendir($dir)) {
while (false !== ($entry = readdir($handle))) {
// 跳过 . 和 ..
if ($entry === '.' || $entry === '..') {
continue;
}
$fullPath = $dir . DIRECTORY_SEPARATOR . $entry;
if (is_dir($fullPath)) {
// 如果是目录,递归扫描
$stats['directories']++;
$subStats = scanDirectoryRecursive($fullPath);
$stats['files'] += $subStats['files'];
$stats['directories'] += $subStats['directories'];
$stats['size'] += $subStats['size'];
} elseif (is_file($fullPath)) {
// 如果是文件,统计大小
$stats['files']++;
$stats['size'] += filesize($fullPath);
}
}
closedir($handle);
}
return $stats;
}
// 使用示例
$directory = "/var/www";
$result = scanDirectoryRecursive($directory);
echo "目录统计信息:<br>";
echo "文件总数: " . number_format($result['files']) . "<br>";
echo "目录总数: " . number_format($result['directories']) . "<br>";
echo "总大小: " . formatBytes($result['size']) . "<br>";
// 辅助函数:格式化字节大小
function formatBytes($bytes, $precision = 2) {
$units = ['B', 'KB', 'MB', 'GB', 'TB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= pow(1024, $pow);
return round($bytes, $precision) . ' ' . $units[$pow];
}
?>
<?php
/**
* 安全地打开并读取目录内容
* @param string $directory 目录路径
* @return array|false 目录内容数组或false(失败时)
*/
function safelyReadDirectory($directory) {
// 验证目录是否存在
if (!file_exists($directory)) {
trigger_error("目录不存在: $directory", E_USER_WARNING);
return false;
}
// 验证是否为目录
if (!is_dir($directory)) {
trigger_error("路径不是目录: $directory", E_USER_WARNING);
return false;
}
// 尝试打开目录
$handle = @opendir($directory);
if ($handle === false) {
$error = error_get_last();
trigger_error("无法打开目录: {$error['message']}", E_USER_WARNING);
return false;
}
$contents = [];
try {
// 读取目录内容
while (false !== ($entry = readdir($handle))) {
// 跳过 . 和 ..
if ($entry === '.' || $entry === '..') {
continue;
}
$fullPath = $directory . DIRECTORY_SEPARATOR . $entry;
// 收集文件信息
$contents[] = [
'name' => $entry,
'type' => is_dir($fullPath) ? 'directory' : 'file',
'size' => is_file($fullPath) ? filesize($fullPath) : null,
'modified' => filemtime($fullPath)
];
}
} catch (Exception $e) {
// 处理读取过程中的异常
trigger_error("读取目录时发生错误: " . $e->getMessage(), E_USER_WARNING);
$contents = false;
} finally {
// 确保关闭目录句柄
if ($handle && is_resource($handle)) {
closedir($handle);
}
}
return $contents;
}
// 使用示例
$directory = "/tmp";
$contents = safelyReadDirectory($directory);
if ($contents !== false) {
echo "目录内容 (" . count($contents) . " 项):<br>";
foreach ($contents as $item) {
echo "{$item['name']} ({$item['type']})<br>";
}
} else {
echo "无法读取目录内容";
}
?>
| 特性 | opendir() + readdir() | scandir() | glob() |
|---|---|---|---|
| 返回类型 | 资源句柄,需要循环读取 | 包含所有条目的数组 | 匹配模式的文件/目录数组 |
| 内存使用 | 低(一次读取一个) | 高(一次性加载) | 中等(加载匹配项) |
| 性能 | 适合大型目录 | 适合小型目录 | 适合模式匹配 |
| 资源管理 | 需要手动关闭 | 自动管理 | 自动管理 |
| 过滤能力 | 手动过滤 | 无过滤 | 支持通配符过滤 |
| 递归支持 | 需要手动实现 | 不支持 | 不支持(但有glob递归扩展) |