使用PHP与Apache实现服务器端文件管理的完整方案
作者:ttod_qzstudio
作为前端开发者,你可能经常需要与服务器文件系统交互,本文将详细介绍如何通过PHP配合Apache实现服务器端文件管理功能,即使你没有任何PHP经验,也能按照本教程实现完整解决方案,需要的朋友可以参考下
引言
作为前端开发者,你可能经常需要与服务器文件系统交互。本文将详细介绍如何通过PHP配合Apache实现服务器端文件管理功能。即使你没有任何PHP经验,也能按照本教程实现完整解决方案!
系统准备
PHP下载与安装
选择与Apache匹配的版本:
- Apache版本:2.4.x
- PHP版本:8.4.x TS (Thread Safe)(对应64位Apache的文件名应该类似:VS17 x64 Thread Safe,对应32位Apache的文件名应该类似:VS17 x86 Thread Safe)
- 架构:x64(64位系统)或x86(32位系统)
解压到目录(如D:\php
),目录结构应包含:
php.exe php8apache2_4.dll php.ini-development ext/ (扩展目录)
Apache配置PHP
编辑conf/httpd.conf
文件:
# 加载PHP模块 LoadModule php_module "D:/php/php8apache2_4.dll" # 指定php.ini目录 PHPIniDir "D:/php" # 将.php文件交给PHP处理 AddHandler application/x-httpd-php .php # 关联文件扩展名 <FilesMatch \.php$> SetHandler application/x-httpd-php </FilesMatch>
验证配置
cd D:\Apache24\bin httpd -t
看到"Syntax OK"表示配置正确,重启Apache服务:
httpd -k restart
文件管理API实现
创建file_manager.php
文件:
<?php header('Content-Type: application/json'); header('Access-Control-Allow-Origin: *'); // 测试用,生产环境应移除 // 安全配置 $BASE_DIR = realpath(__DIR__ . '/Resources'); $ALLOWED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'txt', 'docx']; // 路径验证函数 function validatePath($path) { global $BASE_DIR; $realPath = realpath($BASE_DIR . '/' . $path); return ($realPath && strpos($realPath, $BASE_DIR) === 0) ? $realPath : false; } // 获取目录结构 function listDirectory($path) { $realPath = validatePath($path); if (!$realPath || !is_dir($realPath)) { return ['error' => '无效目录路径']; } $result = []; $items = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($realPath, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::SELF_FIRST ); foreach ($items as $item) { $relativePath = substr($item->getPathname(), strlen($realPath)) ?: '/'; $relativePath = ltrim(str_replace('\\', '/', $relativePath), '/'); $result[] = [ 'name' => $item->getFilename(), 'path' => $relativePath, 'type' => $item->isDir() ? 'directory' : 'file', 'size' => $item->isFile() ? $item->getSize() : 0, 'modified' => date('Y-m-d H:i:s', $item->getMTime()) ]; } return $result; } // 主请求处理 $response = ['status' => 'error', 'message' => '无效请求']; $request = json_decode(file_get_contents('php://input'), true) ?? $_REQUEST; try { if (!isset($request['action'])) { throw new Exception('未指定操作'); } $action = $request['action']; switch ($action) { case 'list': $path = $request['path'] ?? ''; $data = listDirectory($path); $response = ['status' => 'success', 'data' => $data]; break; case 'create-folder': $path = $request['path'] ?? ''; $name = $request['name'] ?? ''; if (empty($name)) throw new Exception('文件夹名称不能为空'); $realPath = validatePath($path); if (!$realPath) throw new Exception('无效路径'); // 清理文件夹名称 $cleanName = preg_replace('/[^a-zA-Z0-9_-]/', '', $name); $newFolder = $realPath . DIRECTORY_SEPARATOR . $cleanName; if (file_exists($newFolder)) { throw new Exception('文件夹已存在'); } if (!mkdir($newFolder, 0755)) { throw new Exception('创建文件夹失败'); } $response = ['status' => 'success', 'message' => '文件夹创建成功']; break; case 'delete-folder': $path = $request['path'] ?? ''; $realPath = validatePath($path); if (!$realPath || !is_dir($realPath)) { throw new Exception('无效目录路径'); } // 安全措施:防止删除根目录 if ($realPath === $BASE_DIR) { throw new Exception('不能删除根目录'); } // 递归删除 $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($realPath, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST ); foreach ($files as $file) { if ($file->isDir()) { rmdir($file->getRealPath()); } else { unlink($file->getRealPath()); } } if (!rmdir($realPath)) { throw new Exception('删除文件夹失败'); } $response = ['status' => 'success', 'message' => '文件夹已删除']; break; case 'rename': $type = $request['type'] ?? 'file'; $path = $request['path'] ?? ''; $newName = $request['newName'] ?? ''; if (empty($newName)) throw new Exception('新名称不能为空'); $realPath = validatePath($path); if (!$realPath) throw new Exception('无效路径'); // 清理新名称 $cleanName = preg_replace('/[^a-zA-Z0-9_.-]/', '', $newName); $newPath = dirname($realPath) . DIRECTORY_SEPARATOR . $cleanName; if (file_exists($newPath)) { throw new Exception('目标名称已存在'); } if (!rename($realPath, $newPath)) { throw new Exception('重命名失败'); } $response = ['status' => 'success', 'message' => '重命名成功']; break; case 'upload-file': $targetPath = $request['path'] ?? ''; $realPath = validatePath($targetPath); if (!$realPath || !is_dir($realPath)) { throw new Exception('无效目标目录'); } if (empty($_FILES['file'])) { throw new Exception('未选择上传文件'); } $file = $_FILES['file']; $filename = preg_replace('/[^a-zA-Z0-9_.-]/', '', basename($file['name'])); $targetFile = $realPath . DIRECTORY_SEPARATOR . $filename; $ext = strtolower(pathinfo($targetFile, PATHINFO_EXTENSION)); // 文件类型验证 if (!in_array($ext, $ALLOWED_EXTENSIONS)) { throw new Exception('不允许的文件类型: ' . $ext); } // 防止文件覆盖 if (file_exists($targetFile)) { $filename = time() . '_' . $filename; $targetFile = $realPath . DIRECTORY_SEPARATOR . $filename; } if (!move_uploaded_file($file['tmp_name'], $targetFile)) { throw new Exception('文件上传失败'); } $response = ['status' => 'success', 'filename' => $filename, 'message' => '文件上传成功']; break; case 'delete-file': $path = $request['path'] ?? ''; $realPath = validatePath($path); if (!$realPath || !is_file($realPath)) { throw new Exception('无效文件路径'); } if (!unlink($realPath)) { throw new Exception('文件删除失败'); } $response = ['status' => 'success', 'message' => '文件已删除']; break; default: throw new Exception('未知操作: ' . $action); } } catch (Exception $e) { $response = ['status' => 'error', 'message' => $e->getMessage()]; } echo json_encode($response); ?>
测试页面
创建file_manager_test.html
:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>文件管理器测试</title> <style> :root { --primary: #3498db; --success: #2ecc71; --danger: #e74c3c; --dark: #34495e; --light: #f8f9fa; } * { box-sizing: border-box; margin: 0; padding: 0; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } body { background-color: #f5f7fa; color: #333; line-height: 1.6; padding: 20px; } .container { max-width: 1200px; margin: 0 auto; background: white; border-radius: 10px; box-shadow: 0 0 20px rgba(0, 0, 0, 0.1); padding: 30px; } header { text-align: center; margin-bottom: 30px; padding-bottom: 20px; border-bottom: 1px solid #eee; } h1 { color: var(--dark); margin-bottom: 10px; } .subtitle { color: #7f8c8d; font-weight: 400; } .section { margin-bottom: 30px; padding: 25px; border: 1px solid #e1e4e8; border-radius: 8px; background: var(--light); transition: all 0.3s ease; } .section:hover { box-shadow: 0 5px 15px rgba(0, 0, 0, 0.05); border-color: #d1d9e0; } .section h2 { color: var(--dark); margin-top: 0; margin-bottom: 20px; padding-bottom: 10px; border-bottom: 1px dashed #ddd; display: flex; align-items: center; } .section h2 i { margin-right: 10px; color: var(--primary); } .form-group { margin-bottom: 20px; display: flex; flex-wrap: wrap; align-items: center; } label { display: inline-block; width: 150px; font-weight: 600; color: #555; } input[type="text"], input[type="file"] { width: calc(100% - 160px); padding: 12px 15px; border: 1px solid #ddd; border-radius: 4px; font-size: 16px; transition: border 0.3s; } input[type="text"]:focus, input[type="file"]:focus { border-color: var(--primary); outline: none; box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.2); } button { padding: 12px 25px; background: var(--primary); color: white; border: none; border-radius: 4px; cursor: pointer; font-size: 16px; font-weight: 600; transition: all 0.3s; margin-top: 10px; } button:hover { background: #2980b9; transform: translateY(-2px); } .delete-btn { background: var(--danger); } .delete-btn:hover { background: #c0392b; } .radio-group { display: flex; gap: 20px; margin-left: 150px; width: calc(100% - 150px); } .radio-group label { width: auto; display: flex; align-items: center; gap: 5px; font-weight: normal; } .response { margin-top: 20px; padding: 15px; border-radius: 4px; display: none; } .success { background-color: rgba(46, 204, 113, 0.1); border: 1px solid var(--success); color: #27ae60; display: block; } .error { background-color: rgba(231, 76, 60, 0.1); border: 1px solid var(--danger); color: #c0392b; display: block; } pre { background: #2d2d2d; color: #f8f8f2; padding: 15px; border-radius: 4px; max-height: 400px; overflow: auto; margin-top: 15px; font-family: 'Consolas', monospace; } @media (max-width: 768px) { .form-group { flex-direction: column; align-items: flex-start; } label { width: 100%; margin-bottom: 8px; } input[type="text"], input[type="file"] { width: 100%; } .radio-group { margin-left: 0; width: 100%; } } </style> </head> <body> <div class="container"> <header> <h1>服务器文件管理器测试</h1> <p class="subtitle">通过PHP与Apache实现安全的服务器端文件操作</p> </header> <!-- 列出目录内容 --> <div class="section"> <h2><i class="fas fa-folder-open"></i> 列出目录内容</h2> <div class="form-group"> <label for="listPath">目录路径:</label> <input type="text" id="listPath" placeholder="例如: docs/images (留空显示根目录)"> </div> <button onclick="listDirectory()">列出目录</button> <div class="response"> <pre id="listResult">目录内容将显示在这里...</pre> </div> </div> <!-- 创建文件夹 --> <div class="section"> <h2><i class="fas fa-folder-plus"></i> 创建文件夹</h2> <div class="form-group"> <label for="createPath">父目录路径:</label> <input type="text" id="createPath" placeholder="例如: docs"> </div> <div class="form-group"> <label for="folderName">文件夹名称:</label> <input type="text" id="folderName" placeholder="例如: new_folder"> </div> <button onclick="createFolder()">创建文件夹</button> <div class="response" id="createResponse"></div> </div> <!-- 删除文件夹 --> <div class="section"> <h2><i class="fas fa-trash-alt"></i> 删除文件夹</h2> <div class="form-group"> <label for="deleteFolderPath">文件夹路径:</label> <input type="text" id="deleteFolderPath" placeholder="例如: docs/old_folder"> </div> <button class="delete-btn" onclick="deleteFolder()">删除文件夹</button> <div class="response" id="deleteFolderResponse"></div> </div> <!-- 重命名文件/文件夹 --> <div class="section"> <h2><i class="fas fa-i-cursor"></i> 重命名项目</h2> <div class="form-group"> <label for="renamePath">当前路径:</label> <input type="text" id="renamePath" placeholder="例如: docs/image.jpg"> </div> <div class="form-group"> <label for="newName">新名称:</label> <input type="text" id="newName" placeholder="例如: new_image.jpg"> </div> <div class="form-group"> <label>类型:</label> <div class="radio-group"> <label><input type="radio" name="renameType" value="file" checked> 文件</label> <label><input type="radio" name="renameType" value="folder"> 文件夹</label> </div> </div> <button onclick="renameItem()">重命名</button> <div class="response" id="renameResponse"></div> </div> <!-- 上传文件 --> <div class="section"> <h2><i class="fas fa-upload"></i> 上传文件</h2> <div class="form-group"> <label for="uploadPath">目标目录:</label> <input type="text" id="uploadPath" placeholder="例如: docs/uploads"> </div> <div class="form-group"> <label for="fileInput">选择文件:</label> <input type="file" id="fileInput"> </div> <button onclick="uploadFile()">上传文件</button> <div class="response" id="uploadResponse"></div> </div> <!-- 删除文件 --> <div class="section"> <h2><i class="fas fa-trash-alt"></i> 删除文件</h2> <div class="form-group"> <label for="deleteFilePath">文件路径:</label> <input type="text" id="deleteFilePath" placeholder="例如: docs/file.txt"> </div> <button class="delete-btn" onclick="deleteFile()">删除文件</button> <div class="response" id="deleteFileResponse"></div> </div> </div> <!-- Font Awesome 图标 --> <script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/js/all.min.js"></script> <script> const API_URL = 'file_manager.php'; // 显示响应消息 function showResponse(element, message, isSuccess = true) { element.textContent = message; element.className = 'response ' + (isSuccess ? 'success' : 'error'); element.style.display = 'block'; // 3秒后淡出 setTimeout(() => { element.style.opacity = '1'; let opacity = 1; const fadeOut = setInterval(() => { opacity -= 0.05; element.style.opacity = opacity; if (opacity <= 0) { clearInterval(fadeOut); element.style.display = 'none'; } }, 50); }, 3000); } // 调用API async function callApi(data, files) { const formData = new FormData(); // 添加表单数据 for (const key in data) { formData.append(key, data[key]); } // 添加文件 if (files) { for (const file of files) { formData.append('file', file); } } try { const response = await fetch(API_URL, { method: 'POST', body: formData }); return await response.json(); } catch (error) { return { status: 'error', message: '网络错误: ' + error.message }; } } // 列出目录内容 async function listDirectory() { const path = document.getElementById('listPath').value || ''; const result = document.getElementById('listResult'); result.textContent = '加载中...'; const response = await callApi({ action: 'list', path: path }); if (response.status === 'success') { result.textContent = JSON.stringify(response.data, null, 2); } else { result.textContent = '错误: ' + response.message; } } // 创建文件夹 async function createFolder() { const path = document.getElementById('createPath').value || ''; const name = document.getElementById('folderName').value; const responseEl = document.getElementById('createResponse'); if (!name) { showResponse(responseEl, '文件夹名称不能为空', false); return; } const response = await callApi({ action: 'create-folder', path: path, name: name }); showResponse( responseEl, response.status === 'success' ? '✅ ' + response.message : '❌ 错误: ' + response.message, response.status === 'success' ); } // 删除文件夹 async function deleteFolder() { const path = document.getElementById('deleteFolderPath').value; const responseEl = document.getElementById('deleteFolderResponse'); if (!path) { showResponse(responseEl, '文件夹路径不能为空', false); return; } if (!confirm(`确定要删除文件夹 "${path}" 及其所有内容吗?`)) { return; } const response = await callApi({ action: 'delete-folder', path: path }); showResponse( responseEl, response.status === 'success' ? '✅ ' + response.message : '❌ 错误: ' + response.message, response.status === 'success' ); } // 重命名项目 async function renameItem() { const path = document.getElementById('renamePath').value; const newName = document.getElementById('newName').value; const type = document.querySelector('input[name="renameType"]:checked').value; const responseEl = document.getElementById('renameResponse'); if (!path || !newName) { showResponse(responseEl, '路径和新名称不能为空', false); return; } const response = await callApi({ action: 'rename', path: path, newName: newName, type: type }); showResponse( responseEl, response.status === 'success' ? '✅ ' + response.message : '❌ 错误: ' + response.message, response.status === 'success' ); } // 上传文件 async function uploadFile() { const path = document.getElementById('uploadPath').value || ''; const fileInput = document.getElementById('fileInput'); const responseEl = document.getElementById('uploadResponse'); if (!fileInput.files || fileInput.files.length === 0) { showResponse(responseEl, '请选择要上传的文件', false); return; } const response = await callApi({ action: 'upload-file', path: path }, [fileInput.files[0]]); showResponse( responseEl, response.status === 'success' ? `✅ ${response.message} - 文件名: ${response.filename}` : '❌ 错误: ' + response.message, response.status === 'success' ); } // 删除文件 async function deleteFile() { const path = document.getElementById('deleteFilePath').value; const responseEl = document.getElementById('deleteFileResponse'); if (!path) { showResponse(responseEl, '文件路径不能为空', false); return; } if (!confirm(`确定要删除文件 "${path}" 吗?`)) { return; } const response = await callApi({ action: 'delete-file', path: path }); showResponse( responseEl, response.status === 'success' ? '✅ ' + response.message : '❌ 错误: ' + response.message, response.status === 'success' ); } </script> </body> </html>
关键注意事项
安全配置
- 生产环境中移除
Access-Control-Allow-Origin: *
- 添加身份验证(如Basic Auth或API密钥)
- 限制允许的文件扩展名
- 设置文件上传大小限制(在php.ini中配置)
权限设置
- Windows:为
Resources
目录添加Apache用户(如SYSTEM
)的完全控制权限
- Linux:
chown -R www-data:www-data Resources && chmod -R 755 Resources
路径说明
- 列出Resources根目录:输入框留空
- 列出子目录:直接输入子目录名(如
uploads
) - 不要使用前导斜杠(如
/uploads
)
测试流程
创建Resources目录并设置权限
上传file_manager.php
和file_manager_test.html
到Apache文档根目录
访问http://localhost/file_manager_test.html
测试各项功能:
- 列出目录(留空)
- 创建测试文件夹
- 上传文件
- 重命名/删除操作
补充:
由于前面的php提供的目录树形式过于扁平,这里提供一个返回树形结构的目录的json的php文件。如果要基于这个php文件进行测试,在测试文件file-manager-test.html里面 action:'list'的地方改成action:'get-tree'就可以了。
改进后的file_manager.php:
<?php header('Content-Type: application/json'); header('Access-Control-Allow-Origin: *'); header('Access-Control-Allow-Methods: POST, GET, OPTIONS'); header('Access-Control-Allow-Headers: Content-Type'); // 配置安全选项 $BASE_DIR = realpath(__DIR__ . '/Resources'); $ALLOWED_EXTENSIONS = ['jpg', 'jpeg', 'png', 'gif', 'glb', 'gltf', 'fbx', 'obj', 'txt', 'md']; // 递归获取目录结构 function getDirectoryTree($path) { $realPath = realpath($path); if (!$realPath || !is_dir($realPath)) { return null; } $result = [ 'name' => basename($realPath), 'path' => str_replace($GLOBALS['BASE_DIR'], '', $realPath) ?: '/', 'type' => 'directory', 'children' => [] ]; $items = scandir($realPath); foreach ($items as $item) { if ($item === '.' || $item === '..') continue; $itemPath = $realPath . DIRECTORY_SEPARATOR . $item; if (is_dir($itemPath)) { $result['children'][] = getDirectoryTree($itemPath); } else { $ext = strtolower(pathinfo($itemPath, PATHINFO_EXTENSION)); $result['children'][] = [ 'name' => $item, 'path' => str_replace($GLOBALS['BASE_DIR'], '', $itemPath), 'type' => 'file', 'size' => filesize($itemPath), 'modified' => filemtime($itemPath), 'extension' => $ext ]; } } return $result; } // 处理请求 $response = ['status' => 'error', 'message' => 'Invalid request']; $request = json_decode(file_get_contents('php://input'), true) ?? $_REQUEST; try { if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') { exit(0); } if (!isset($request['action'])) { throw new Exception('No action specified'); } $action = $request['action']; switch ($action) { case 'get-tree': $tree = getDirectoryTree($BASE_DIR); if ($tree) { $response = ['status' => 'success', 'data' => $tree]; } else { throw new Exception('Failed to load directory tree'); } break; case 'get-files': $path = $request['path'] ?? '/'; $realPath = realpath($BASE_DIR . $path); if (!$realPath || !is_dir($realPath)) { throw new Exception('Invalid directory path'); } $files = []; $items = scandir($realPath); foreach ($items as $item) { if ($item === '.' || $item === '..') continue; $itemPath = $realPath . DIRECTORY_SEPARATOR . $item; if (!is_dir($itemPath)) { $ext = strtolower(pathinfo($itemPath, PATHINFO_EXTENSION)); $files[] = [ 'name' => $item, 'path' => $path . '/' . $item, 'type' => 'file', 'size' => filesize($itemPath), 'modified' => filemtime($itemPath), 'extension' => $ext ]; } } $response = ['status' => 'success', 'files' => $files]; break; case 'create-folder': $path = $request['path'] ?? ''; $name = $request['name'] ?? ''; if (empty($name)) throw new Exception('Folder name is required'); $realPath = validatePath($path); if (!$realPath) throw new Exception('Invalid path'); $newFolder = $realPath . DIRECTORY_SEPARATOR . preg_replace('/[^a-zA-Z0-9_-]/', '', $name); if (file_exists($newFolder)) { throw new Exception('Folder already exists'); } if (!mkdir($newFolder)) { throw new Exception('Failed to create folder'); } $response = ['status' => 'success', 'message' => 'Folder created']; break; case 'delete-folder': $path = $request['path'] ?? ''; $realPath = validatePath($path); if (!$realPath || !is_dir($realPath)) { throw new Exception('Invalid directory path'); } // 安全措施:防止删除根目录 if ($realPath === $BASE_DIR) { throw new Exception('Cannot delete base directory'); } // 递归删除目录 $files = new RecursiveIteratorIterator( new RecursiveDirectoryIterator($realPath, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST ); foreach ($files as $file) { if ($file->isDir()) { rmdir($file->getRealPath()); } else { unlink($file->getRealPath()); } } if (!rmdir($realPath)) { throw new Exception('Failed to delete folder'); } $response = ['status' => 'success', 'message' => 'Folder deleted']; break; case 'rename': $type = $request['type'] ?? ''; // 'file' or 'folder' $path = $request['path'] ?? ''; $newName = $request['newName'] ?? ''; if (empty($newName)) throw new Exception('New name is required'); $realPath = validatePath($path); if (!$realPath) throw new Exception('Invalid path'); $newPath = dirname($realPath) . DIRECTORY_SEPARATOR . preg_replace('/[^a-zA-Z0-9_.-]/', '', $newName); if (file_exists($newPath)) { throw new Exception('Target name already exists'); } if (!rename($realPath, $newPath)) { throw new Exception('Rename failed'); } $response = ['status' => 'success', 'message' => 'Renamed successfully']; break; case 'upload-file': $targetPath = $request['path'] ?? ''; $realPath = validatePath($targetPath); if (!$realPath || !is_dir($realPath)) { throw new Exception('Invalid target directory'); } if (empty($_FILES['file'])) { throw new Exception('No file uploaded'); } $file = $_FILES['file']; $filename = preg_replace('/[^a-zA-Z0-9_.-]/', '', basename($file['name'])); $targetFile = $realPath . DIRECTORY_SEPARATOR . $filename; $ext = strtolower(pathinfo($targetFile, PATHINFO_EXTENSION)); // 验证文件类型 if (!in_array($ext, $ALLOWED_EXTENSIONS)) { throw new Exception('File type not allowed'); } // 防止覆盖现有文件 if (file_exists($targetFile)) { $filename = time() . '_' . $filename; $targetFile = $realPath . DIRECTORY_SEPARATOR . $filename; } if (!move_uploaded_file($file['tmp_name'], $targetFile)) { throw new Exception('File upload failed'); } $response = ['status' => 'success', 'filename' => $filename, 'message' => 'File uploaded']; break; case 'delete-file': $path = $request['path'] ?? ''; $realPath = validatePath($path); if (!$realPath || !is_file($realPath)) { throw new Exception('Invalid file path'); } if (!unlink($realPath)) { throw new Exception('File deletion failed'); } $response = ['status' => 'success', 'message' => 'File deleted']; break; default: throw new Exception('Unknown action'); } } catch (Exception $e) { $response = ['status' => 'error', 'message' => $e->getMessage()]; } echo json_encode($response); ?>
以上就是使用PHP与Apache实现服务器端文件管理的完整方案的详细内容,更多关于PHP Apache服务器端文件管理的资料请关注脚本之家其它相关文章!