php文件和文件夹操作类
文件和文件夹操作
移动 | 复制 | 删除 | 重命名 | 下载
<?php
namespace Framework\Tools;
use PharData;
class FileManager
{
// 提示信息配置
private $messages = array(
'file_list_success' => '获取成功。',
'file_list_failure' => '获取失败。',
'not_exist' => '文件夹不存在。',
'duplicate_name' => '文件或文件夹名称已存在。',
'not_duplicate_name' => '文件或文件夹名称不存在。',
'file_name' => '文件名称已存在.',
'folder_name' => '文件夹名称已存在。',
'copy_success' => '复制成功。',
'copy_failure' => '复制失败。',
'move_success' => '移动成功。',
'move_failure' => '移动失败。',
'rename_success' => '重命名成功。',
'rename_failure' => '重命名失败。',
'recycle_duplicate_name' => '回收站中已存在当前文件或文件夹。',
'delete_to_recycle_success' => '成功移动到回收站。',
'delete_to_recycle_failure' => '移动到回收站失败。'
);
// 存储最近一次操作的消息
private $lastMessage = '';
/**
* 设置提示信息
*
* @param array $messages 自定义提示信息数组,键为消息类型,值为具体消息内容
* @return void
*/
public function setMessages($messages)
{
$this->messages = array_merge($this->messages, $messages);
}
/**
* 获取最近一次操作的消息
*
* @return string 最近一次操作的提示消息
*/
public function getLastMessage()
{
return $this->lastMessage;
}
/**
* 获取当前文件夹下的所有文件夹及子文件夹的树形结构
*
* @param string $dir 目录路径
* @return array
*/
public function getDirectoryTree($dir, $path = '')
{
$result = [];
if (!is_dir($dir)) {
$this->lastMessage = $this->messages['not_exist'];
return $result;
}
$cdir = scandir($dir);
foreach ($cdir as $key => $value) {
if (!in_array($value, [".", ".."])) {
$fullPath = $dir . DIRECTORY_SEPARATOR . $value;
if (is_dir($fullPath)) {
$result[] = [
'label' => $value, // 文件夹名称
'path' => $path ? str_replace($path, '', $fullPath) : $fullPath,
'children' => $this->getDirectoryTree($fullPath, $path),
];
}
}
}
$this->lastMessage = $this->messages['file_list_success'];
return $result;
}
/**
* 获取指定文件夹下的所有文件(可配置后缀名过滤)
*
* @param string $dir 目录路径
* @param array $allowedExtensions 允许的文件后缀名(可选)
* @return array
*/
public function getFilesInDirectory($dir, $allowedExtensions = [])
{
$files = [];
if (!file_exists($dir) && !is_dir($dir)) {
$this->lastMessage = $this->messages['not_exist'];
return $files;
}
$cdir = scandir($dir);
foreach ($cdir as $key => $value) {
if (!in_array($value, [".", ".."]) && is_file($dir . DIRECTORY_SEPARATOR . $value)) {
// 如果没有设置后缀名过滤,或者文件后缀名在允许的列表中
if (empty($allowedExtensions) || $this->isFileExtensionAllowed($value, $allowedExtensions)) {
$files[] = $value;
}
}
}
$this->lastMessage = $this->messages['file_list_success'];
return $files;
}
/**
* 检查文件后缀名是否在允许的列表中
*
* @param string $fileName 文件名
* @param array $allowedExtensions 允许的后缀名列表
* @return bool
*/
public function isFileExtensionAllowed($fileName, $allowedExtensions)
{
$extension = pathinfo($fileName, PATHINFO_EXTENSION);
return in_array(strtolower($extension), array_map('strtolower', $allowedExtensions));
}
/**
* 递归删除目录及其内容
*
* @param string $dir 目录路径
* @return bool 是否删除成功
*/
private function deleteDir($dir)
{
if (!is_dir($dir)) return false;
foreach (scandir($dir) as $item) {
if ($item == '.' || $item == '..') continue;
$fullPath = "$dir/$item";
is_dir($fullPath) ? $this->deleteDir($fullPath) : unlink($fullPath);
}
return rmdir($dir);
}
/**
* 删除文件夹
* emptyTheRecycleBin 是否清空回收站
* @param string $dir
* @return bool
*/
public function deleteDirectory($dir)
{
if (!file_exists($dir)) {
return true;
}
if (!is_dir($dir)) {
return unlink($dir);
}
foreach (scandir($dir) as $item) {
if ($item == '.' || $item == '..') {
continue;
}
if (!$this->deleteDirectory($dir . DIRECTORY_SEPARATOR . $item)) {
return false;
}
}
return rmdir($dir);
}
/**
* 复制单个或多个文件或文件夹
*
* @param string|array $sources 要复制的源文件或文件夹路径,可以是单个路径字符串或路径数组
* @param string $destination 目标文件夹路径
* @return bool 操作成功返回 true,失败返回 false
*/
public function copy($sources, $destination)
{
if (!$this->validateSources($sources, $destination)) {
return false;
}
if (!is_array($sources)) {
$sources = array($sources);
}
foreach ($sources as $source) {
if (is_file($source)) {
if (!copy($source, $destination . '/' . basename($source))) {
$this->lastMessage = $this->messages['copy_failure'];
return false;
}
} elseif (is_dir($source)) {
if (!$this->recursiveCopy($source, $destination . '/' . basename($source))) {
$this->lastMessage = $this->messages['copy_failure'];
return false;
}
}
}
$this->lastMessage = $this->messages['copy_success'];
return true;
}
/**
* 递归复制文件夹
*
* @param string $source 源文件夹路径
* @param string $destination 目标文件夹路径
* @return bool 复制成功返回 true
*/
private function recursiveCopy($source, $destination)
{
if (!is_dir($destination)) {
mkdir($destination);
}
$dir = opendir($source);
while (($file = readdir($dir)) !== false) {
if ($file != '.' && $file != '..') {
if (is_dir($source . '/' . $file)) {
$this->recursiveCopy($source . '/' . $file, $destination . '/' . $file);
} else {
copy($source . '/' . $file, $destination . '/' . $file);
}
}
}
closedir($dir);
return true;
}
/**
* 移动单个或多个文件或文件夹
*
* @param string|array $sources 要移动的源文件或文件夹路径,可以是单个路径字符串或路径数组
* @param string $destination 目标文件夹路径
* @return bool 操作成功返回 true,失败返回 false
*/
public function move($sources, $destination)
{
if (!$this->validateSources($sources, $destination)) {
return false;
}
if (!is_array($sources)) {
$sources = array($sources);
}
foreach ($sources as $source) {
if (!rename($source, $destination . '/' . basename($source))) {
$this->lastMessage = $this->messages['move_failure'];
return false;
}
}
$this->lastMessage = $this->messages['move_success'];
return true;
}
/**
* 重命名文件或文件夹
*
* @param string $path 要重命名的文件或文件夹的当前路径
* @param string $newName 新的名称
* @return bool 重命名成功返回 true,失败返回 false
*/
public function rename($path, $newName)
{
$dir = dirname($path);
if (file_exists($dir . '/' . $newName)) {
$this->lastMessage = $this->messages['duplicate_name'];
return false;
}
if (!rename($path, $dir . '/' . $newName)) {
$this->lastMessage = $this->messages['rename_failure'];
return false;
}
$this->lastMessage = $this->messages['rename_success'];
return true;
}
/**
* 生成唯一的文件名
*
* @param string $baseName 基础文件名
* @param string $recycleBin 回收站目录
* @return string 唯一的文件名
*/
private function getUniqueFileName($baseName, $recycleBin)
{
$nameInfo = pathinfo($baseName);
$name = $nameInfo['filename'];
$ext = isset($nameInfo['extension']) ? '.' . $nameInfo['extension'] : '';
$counter = 1;
$newName = $baseName;
while (file_exists($recycleBin . '/' . $newName)) {
$newName = $name . " ($counter)" . $ext;
$counter++;
}
return $newName;
}
/**
* 删除文件或文件夹到回收站
*
* @param string|array $sources 要删除到回收站的文件或文件夹路径,可以是单个路径字符串或路径数组
* @param string $recycleBin 回收站目录
* @return bool 操作成功返回 true,失败返回 false
*/
public function deleteToRecycle($sources, $recycleBin)
{
if (!is_array($sources)) {
$sources = array($sources);
}
foreach ($sources as $source) {
$baseName = basename($source);
if (!file_exists($source)) {
$this->lastMessage = $this->messages['not_duplicate_name'];
return false;
}
$uniqueName = $this->getUniqueFileName($baseName, $recycleBin);
$destination = $recycleBin . '/' . $uniqueName;
if (!rename($source, $destination)) {
$this->lastMessage = $this->messages['delete_to_recycle_failure'];
return false;
}
}
$this->lastMessage = $this->messages['delete_to_recycle_success'];
return true;
}
/**
* 验证源文件或文件夹是否可以操作
*
* @param string|array $sources 要验证的源文件或文件夹路径,可以是单个路径字符串或路径数组
* @param string $destination 目标文件夹路径
* @return bool 验证通过返回 true,存在重名情况返回 false
*/
private function validateSources($sources, $destination)
{
if (!is_array($sources)) {
$sources = array($sources);
}
if (!file_exists($destination) && !is_dir($destination)) {
$this->lastMessage = $this->messages['not_exist'];
return false;
}
foreach ($sources as $source) {
if (file_exists($destination . '/' . basename($source))) {
if (is_file($destination . '/' . basename($source))) {
$this->lastMessage = $this->messages['file_name'];
} elseif (is_dir($destination . '/' . basename($source))) {
$this->lastMessage = $this->messages['folder_name'];
} else {
$this->lastMessage = $this->messages['duplicate_name'];
}
return false;
}
}
return true;
}
/**
* 获取文件详细信息
*
* @param string $filePath
* @return array
*/
public function getFileDetails($filePath)
{
return [
'size' => filesize($filePath),
'last_modified' => date("Y m d H:i:s.", filemtime($filePath)),
'type' => filetype($filePath),
'extension' => pathinfo($filePath, PATHINFO_EXTENSION),
];
}
/**
* 获取文件夹详细信息
*
* @param string $dir
* @return array
*/
public function getDirectoryDetails($dir)
{
return [
'size' => $this->getDirectorySize($dir),
'last_modified' => date("Y m d H:i:s.", filemtime($dir)),
'type' => filetype($dir),
];
}
/**
* 计算文件夹大小
*
* @param string $dir
* @return int
*/
private function getDirectorySize($dir)
{
$size = 0;
foreach (scandir($dir) as $item) {
if ($item == '.' || $item == '..') {
continue;
}
$path = $dir . DIRECTORY_SEPARATOR . $item;
if (is_file($path)) {
$size += filesize($path);
} else {
$size += $this->getDirectorySize($path);
}
}
return $size;
}
/**
* 下载文件或文件夹
*
* @param array|string $paths 文件或文件夹路径(可以是单个路径或路径数组)
* @param string $archiveName 打包下载的文件名(可选)
* @return bool
*/
public function download($paths, $archiveName = 'download.tar.gz')
{
if (empty($paths)) {
return $this->returnError("没有文件可供下载");
}
// 如果是单个文件,直接下载
if (is_string($paths)) {
if (!file_exists($paths)) {
return $this->returnError("文件不存在");
}
$this->downloadFile($paths);
return true;
}
// 如果是多个文件或文件夹,检查是否存在
foreach ($paths as $path) {
if (!file_exists($path)) {
return $this->returnError("文件或文件夹不存在");
}
}
// 打包下载
$this->downloadAsTarGz($paths, $archiveName);
return true;
}
/**
* 下载文件或图片的方法
*
* @param string $filePath 文件的完整路径
* @param string $fileName 下载时显示的文件名,默认为空
* @return void
*/
private function downloadFile($filePath, $fileName = '')
{
// 检查文件是否存在
if (!file_exists($filePath)) {
http_response_code(404);
die("文件不存在");
}
// 如果没有提供文件名,则使用文件的原始名称
$fileName = $fileName ?: basename($filePath);
// 获取文件的 MIME 类型
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $filePath);
finfo_close($finfo);
// 设置响应头
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($filePath));
// 开启输出缓冲
ob_clean();
flush();
// 读取并输出文件内容
readfile($filePath);
// 终止脚本执行
exit;
}
/**
* 打包下载多个文件或文件夹为 tar.gz 格式
*
* @param array $paths 文件或文件夹路径数组
* @param string $archiveName 打包下载的文件名
*/
private function downloadAsTarGz($paths, $archiveName = 'download.tar.gz')
{
// 创建临时文件
$archivePath = sys_get_temp_dir() . '/' . $archiveName;
// 使用 Linux 命令打包
if ($this->isLinux()) {
$fileList = implode(' ', array_map('escapeshellarg', $paths));
exec("tar -czf '{$archivePath}' -C " . dirname($paths[0]) . " " . $fileList);
if (file_exists($archivePath)) {
$this->downloadFile($archivePath, $archiveName);
unlink($archivePath); // 删除临时文件
exit;
} else {
die("打包文件失败");
}
}
// 使用 PHP 打包
$phar = new PharData($archivePath);
foreach ($paths as $path) {
if (is_dir($path)) {
$phar->buildFromDirectory($path);
} else {
$phar->addFile($path, basename($path));
}
}
if (file_exists($archivePath)) {
$this->downloadFile($archivePath, $archiveName);
unlink($archivePath); // 删除临时文件
exit;
} else {
die("打包文件失败");
}
}
/**
* 判断是否是 Linux 系统
*
* @return bool
*/
private function isLinux()
{
return strtoupper(substr(PHP_OS, 0, 3)) === 'LIN';
}
/**
* 返回错误信息
*
* @param string $message 错误信息
* @return bool
*/
private function returnError($message)
{
echo $message;
return false;
}
}

浙公网安备 33010602011771号