本文目录导读:

我将为您设计一个PHP项目文件管理与预览系统,这个系统将包含文件浏览、上传、删除、预览等功能,并支持多种文件类型的预览。
完整文件管理预览系统
目录结构
file-manager/
├── index.php # 主入口
├── config.php # 配置文件
├── functions.php # 功能函数
├── css/
│ └── style.css # 样式文件
├── js/
│ └── main.js # JavaScript文件
└── uploads/ # 上传目录
配置文件 (config.php)
<?php
// 配置文件
define('BASE_PATH', __DIR__);
define('UPLOAD_DIR', BASE_PATH . '/uploads/');
define('MAX_FILE_SIZE', 50 * 1024 * 1024); // 50MB
// 允许的文件类型
$allowed_types = [
'image' => ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp'],
'document' => ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt'],
'video' => ['mp4', 'avi', 'mov', 'wmv', 'flv'],
'audio' => ['mp3', 'wav', 'ogg', 'flac'],
'archive' => ['zip', 'rar', 'tar', 'gz', '7z'],
'code' => ['php', 'html', 'css', 'js', 'json', 'xml']
];
// 获取所有允许的文件类型
$all_extensions = [];
foreach ($allowed_types as $type => $extensions) {
$all_extensions = array_merge($all_extensions, $extensions);
}
// 文件图标映射
$file_icons = [
'image' => '🖼️',
'document' => '📄',
'video' => '🎬',
'audio' => '🎵',
'archive' => '📦',
'code' => '💻',
'default' => '📁'
];
功能函数 (functions.php)
<?php
require_once 'config.php';
// 获取文件类型分组
function getFileType($extension) {
global $allowed_types;
foreach ($allowed_types as $type => $extensions) {
if (in_array(strtolower($extension), $extensions)) {
return $type;
}
}
return 'default';
}
// 获取文件图标
function getFileIcon($filename) {
global $file_icons;
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$type = getFileType($extension);
return isset($file_icons[$type]) ? $file_icons[$type] : $file_icons['default'];
}
// 获取可预览的文件类型
function isPreviewable($filename) {
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$previewable_types = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp',
'pdf', 'txt', 'html', 'css', 'js', 'php', 'json', 'xml'];
return in_array($extension, $previewable_types);
}
// 格式化文件大小
function formatFileSize($bytes) {
if ($bytes === 0) return '0 B';
$k = 1024;
$sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
$i = floor(log($bytes) / log($k));
return round($bytes / pow($k, $i), 2) . ' ' . $sizes[$i];
}
// 扫描目录
function scanDirectory($dir) {
$files = [];
if (!file_exists($dir)) {
mkdir($dir, 0777, true);
return $files;
}
$items = scandir($dir);
foreach ($items as $item) {
if ($item == '.' || $item == '..') continue;
$path = $dir . '/' . $item;
$stat = stat($path);
$files[] = [
'name' => $item,
'path' => $path,
'size' => is_file($path) ? filesize($path) : 0,
'modified' => filemtime($path),
'is_dir' => is_dir($path),
'extension' => pathinfo($item, PATHINFO_EXTENSION),
'icon' => is_dir($path) ? '📁' : getFileIcon($item)
];
}
// 按类型排序:目录在前,文件在后,按名称排序
usort($files, function($a, $b) {
if ($a['is_dir'] != $b['is_dir']) {
return $b['is_dir'] - $a['is_dir'];
}
return strnatcasecmp($a['name'], $b['name']);
});
return $files;
}
// 文件上传处理
function handleFileUpload($file, $target_dir) {
if (!is_dir($target_dir)) {
mkdir($target_dir, 0777, true);
}
$filename = basename($file['name']);
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
// 验证文件类型
global $all_extensions;
if (!in_array($extension, $all_extensions)) {
return ['success' => false, 'message' => '不允许的文件类型'];
}
// 验证文件大小
if ($file['size'] > MAX_FILE_SIZE) {
return ['success' => false, 'message' => '文件太大'];
}
// 防止文件名冲突
$target_path = $target_dir . '/' . $filename;
$counter = 1;
while (file_exists($target_path)) {
$info = pathinfo($filename);
$target_path = $target_dir . '/' . $info['filename'] . '_' . $counter . '.' . $info['extension'];
$counter++;
}
if (move_uploaded_file($file['tmp_name'], $target_path)) {
return ['success' => true, 'message' => '上传成功', 'file' => basename($target_path)];
} else {
return ['success' => false, 'message' => '上传失败'];
}
}
// 删除文件
function deleteFile($path) {
if (!file_exists($path)) {
return ['success' => false, 'message' => '文件不存在'];
}
if (is_dir($path)) {
// 删除目录及其内容
if (deleteDirectory($path)) {
return ['success' => true, 'message' => '目录删除成功'];
} else {
return ['success' => false, 'message' => '目录删除失败'];
}
} else {
if (unlink($path)) {
return ['success' => true, 'message' => '文件删除成功'];
} else {
return ['success' => false, 'message' => '文件删除失败'];
}
}
}
// 递归删除目录
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 (!deleteDirectory($dir . DIRECTORY_SEPARATOR . $item)) {
return false;
}
}
return rmdir($dir);
}
// 创建目录
function createDirectory($base_dir, $dir_name) {
$path = $base_dir . '/' . basename($dir_name);
if (file_exists($path)) {
return ['success' => false, 'message' => '目录已存在'];
}
if (mkdir($path, 0777, true)) {
return ['success' => true, 'message' => '目录创建成功'];
} else {
return ['success' => false, 'message' => '目录创建失败'];
}
}
// 处理AJAX请求
function handleAjaxRequest() {
$action = isset($_POST['action']) ? $_POST['action'] : '';
switch ($action) {
case 'list':
$dir = isset($_POST['path']) ? $_POST['path'] : UPLOAD_DIR;
$files = scanDirectory($dir);
echo json_encode(['success' => true, 'files' => $files, 'current_path' => realpath($dir)]);
break;
case 'upload':
if (isset($_FILES['file'])) {
$target_dir = isset($_POST['path']) ? $_POST['path'] : UPLOAD_DIR;
$result = handleFileUpload($_FILES['file'], $target_dir);
echo json_encode($result);
} else {
echo json_encode(['success' => false, 'message' => '没有文件']);
}
break;
case 'delete':
$path = isset($_POST['path']) ? $_POST['path'] : '';
if ($path && strpos(realpath($path), realpath(UPLOAD_DIR)) === 0) {
$result = deleteFile($path);
echo json_encode($result);
} else {
echo json_encode(['success' => false, 'message' => '无效的路径']);
}
break;
case 'create_dir':
$base_dir = isset($_POST['base_path']) ? $_POST['base_path'] : UPLOAD_DIR;
$dir_name = isset($_POST['dir_name']) ? $_POST['dir_name'] : '';
if ($dir_name) {
$result = createDirectory($base_dir, $dir_name);
echo json_encode($result);
} else {
echo json_encode(['success' => false, 'message' => '请输入目录名']);
}
break;
case 'preview':
$path = isset($_POST['path']) ? $_POST['path'] : '';
if ($path && file_exists($path) && is_file($path)) {
$content = getPreviewContent($path);
echo json_encode(['success' => true, 'content' => $content]);
} else {
echo json_encode(['success' => false, 'message' => '文件不存在']);
}
break;
default:
echo json_encode(['success' => false, 'message' => '未知操作']);
}
exit;
}
// 获取预览内容
function getPreviewContent($path) {
$extension = strtolower(pathinfo($path, PATHINFO_EXTENSION));
// 图片预览
if (in_array($extension, ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp'])) {
// 获取相对路径用于生成URL
$relative_path = str_replace($_SERVER['DOCUMENT_ROOT'], '', realpath($path));
return '<img src="' . htmlspecialchars($relative_path) . '" alt="预览" style="max-width:100%;max-height:80vh;">';
}
// PDF预览
if ($extension === 'pdf') {
$relative_path = str_replace($_SERVER['DOCUMENT_ROOT'], '', realpath($path));
return '<iframe src="' . htmlspecialchars($relative_path) . '" style="width:100%;height:80vh;" frameborder="0"></iframe>';
}
// 文本文件预览
if (in_array($extension, ['txt', 'php', 'html', 'css', 'js', 'json', 'xml'])) {
$content = file_get_contents($path);
$escaped_content = htmlspecialchars($content);
return '<pre style="background:#f4f4f4;padding:15px;border-radius:4px;overflow:auto;max-height:80vh;"><code>' . $escaped_content . '</code></pre>';
}
return '<p>无法预览此文件类型</p>';
}
// 处理请求
if (isset($_POST['action'])) {
handleAjaxRequest();
}
主页面 (index.php)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">文件管理系统</title>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<div class="container">
<header>
<h1>📁 文件管理系统</h1>
<div class="header-actions">
<button onclick="showCreateDirModal()" class="btn btn-primary">📂 新建文件夹</button>
<label class="btn btn-success upload-btn">
📤 上传文件
<input type="file" id="fileInput" multiple onchange="uploadFiles()" style="display:none">
</label>
</div>
</header>
<nav class="breadcrumb" id="breadcrumb">
<!-- 面包屑导航会通过JavaScript动态生成 -->
</nav>
<div class="file-grid" id="fileGrid">
<!-- 文件列表会通过JavaScript动态生成 -->
</div>
<div id="loading" class="loading" style="display:none">
<div class="spinner"></div>
<span>加载中...</span>
</div>
</div>
<!-- 预览弹窗 -->
<div id="previewModal" class="modal">
<div class="modal-content">
<span class="close" onclick="closePreview()">×</span>
<div id="previewContent"></div>
</div>
</div>
<!-- 新建文件夹弹窗 -->
<div id="createDirModal" class="modal">
<div class="modal-content">
<span class="close" onclick="closeCreateDirModal()">×</span>
<h3>新建文件夹</h3>
<input type="text" id="dirNameInput" placeholder="请输入文件夹名称">
<button onclick="createDirectory()" class="btn btn-primary">创建</button>
</div>
</div>
<!-- 右键菜单 -->
<div id="contextMenu" class="context-menu">
<ul>
<li onclick="previewFile()">👁️ 预览</li>
<li onclick="downloadFile()">⬇️ 下载</li>
<li onclick="deleteFile()">🗑️ 删除</li>
</ul>
</div>
<div id="notification" class="notification"></div>
<script src="js/main.js"></script>
</body>
</html>
CSS样式 (css/style.css)
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: #f0f2f5;
color: #333;
line-height: 1.6;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
}
header {
background: white;
padding: 20px 30px;
border-radius: 12px;
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
h1 {
font-size: 1.8em;
color: #1a73e8;
}
.header-actions {
display: flex;
gap: 10px;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 6px;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.3s;
display: inline-flex;
align-items: center;
gap: 5px;
}
.btn-primary {
background: #1a73e8;
color: white;
}
.btn-primary:hover {
background: #1557b0;
}
.btn-success {
background: #34a853;
color: white;
}
.btn-success:hover {
background: #2d9249;
}
.btn-danger {
background: #ea4335;
color: white;
}
.btn-danger:hover {
background: #d33426;
}
.upload-btn {
cursor: pointer;
}
.breadcrumb {
background: white;
padding: 12px 20px;
border-radius: 8px;
margin-bottom: 20px;
box-shadow: 0 1px 4px rgba(0,0,0,0.08);
}
.breadcrumb a {
color: #1a73e8;
text-decoration: none;
}
.breadcrumb a:hover {
text-decoration: underline;
}
.breadcrumb span {
color: #666;
margin: 0 5px;
}
.file-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 20px;
}
.file-item {
background: white;
border-radius: 10px;
padding: 20px 15px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 1px 4px rgba(0,0,0,0.08);
position: relative;
}
.file-item:hover {
transform: translateY(-3px);
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.file-item .file-icon {
font-size: 48px;
display: block;
margin-bottom: 10px;
}
.file-item .file-name {
font-size: 13px;
color: #333;
word-break: break-all;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.file-item .file-size {
font-size: 11px;
color: #999;
margin-top: 5px;
}
.file-item .file-date {
font-size: 11px;
color: #999;
}
.file-item.selected {
background: #e8f0fe;
border: 2px solid #1a73e8;
}
.loading {
text-align: center;
padding: 40px;
color: #666;
}
.spinner {
display: inline-block;
width: 40px;
height: 40px;
border: 4px solid #f3f3f3;
border-top: 4px solid #1a73e8;
border-radius: 50%;
animation: spin 1s linear infinite;
margin-bottom: 10px;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* 弹窗样式 */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0,0,0,0.6);
z-index: 1000;
animation: fadeIn 0.3s;
}
.modal-content {
background: white;
margin: 5% auto;
padding: 30px;
border-radius: 12px;
max-width: 800px;
width: 90%;
position: relative;
animation: slideIn 0.3s;
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes slideIn {
from { transform: translateY(-50px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.close {
position: absolute;
right: 20px;
top: 15px;
font-size: 28px;
cursor: pointer;
color: #999;
}
.close:hover {
color: #333;
}
#previewContent {
margin-top: 20px;
}
#dirNameInput {
width: 100%;
padding: 12px;
margin: 15px 0;
border: 2px solid #ddd;
border-radius: 6px;
font-size: 16px;
}
#dirNameInput:focus {
border-color: #1a73e8;
outline: none;
}
/* 右键菜单 */
.context-menu {
display: none;
position: fixed;
background: white;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 999;
min-width: 150px;
}
.context-menu ul {
list-style: none;
padding: 8px 0;
}
.context-menu li {
padding: 10px 20px;
cursor: pointer;
transition: background 0.2s;
}
.context-menu li:hover {
background: #f0f2f5;
}
/* 通知 */
.notification {
position: fixed;
top: 20px;
right: 20px;
padding: 15px 25px;
border-radius: 8px;
color: white;
z-index: 2000;
animation: slideInRight 0.3s;
display: none;
}
.notification.success {
background: #34a853;
}
.notification.error {
background: #ea4335;
}
@keyframes slideInRight {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
/* 响应式设计 */
@media (max-width: 768px) {
header {
flex-direction: column;
gap: 15px;
text-align: center;
}
.file-grid {
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 15px;
}
.file-item .file-icon {
font-size: 36px;
}
}
JavaScript功能 (js/main.js)
// 当前位置
let currentPath = '';
let selectedFile = null;
// 页面加载时初始化
document.addEventListener('DOMContentLoaded', function() {
loadFiles('');
// 点击其他地方关闭右键菜单
document.addEventListener('click', function(e) {
const contextMenu = document.getElementById('contextMenu');
if (contextMenu.style.display === 'block') {
contextMenu.style.display = 'none';
}
});
// 点击弹窗外部关闭
window.onclick = function(event) {
const previewModal = document.getElementById('previewModal');
const createDirModal = document.getElementById('createDirModal');
if (event.target == previewModal) {
closePreview();
}
if (event.target == createDirModal) {
closeCreateDirModal();
}
};
// 允许文件夹拖放上传
setupDragAndDrop();
});
// 加载文件列表
function loadFiles(path) {
currentPath = path;
showLoading(true);
const formData = new FormData();
formData.append('action', 'list');
formData.append('path', path);
fetch('index.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
showLoading(false);
if (data.success) {
renderFileGrid(data.files);
renderBreadcrumb(data.current_path);
} else {
showNotification('加载文件失败', 'error');
}
})
.catch(error => {
showLoading(false);
showNotification('网络错误', 'error');
});
}
// 渲染文件网格
function renderFileGrid(files) {
const grid = document.getElementById('fileGrid');
grid.innerHTML = '';
if (files.length === 0) {
grid.innerHTML = '<div class="empty-state">📂 此文件夹为空</div>';
return;
}
files.forEach(file => {
const item = document.createElement('div');
item.className = 'file-item';
item.dataset.path = file.path;
item.dataset.isDir = file.is_dir;
item.dataset.name = file.name;
item.innerHTML = `
<span class="file-icon">${file.icon}</span>
<div class="file-name">${file.name}</div>
<div class="file-size">${file.is_dir ? '文件夹' : formatFileSize(file.size)}</div>
<div class="file-date">${formatDate(file.modified)}</div>
`;
// 双击进入目录或预览文件
item.addEventListener('dblclick', function(e) {
if (file.is_dir) {
loadFiles(file.path);
} else {
previewFile(file.path);
}
});
// 单击选择
item.addEventListener('click', function(e) {
document.querySelectorAll('.file-item').forEach(el => el.classList.remove('selected'));
this.classList.add('selected');
selectedFile = file;
});
// 右键菜单
item.addEventListener('contextmenu', function(e) {
e.preventDefault();
selectedFile = file;
showContextMenu(e.pageX, e.pageY);
});
grid.appendChild(item);
});
}
// 渲染面包屑导航
function renderBreadcrumb(path) {
const breadcrumb = document.getElementById('breadcrumb');
const parts = path.split('/');
let html = '';
// 根目录
html += '<a href="#" onclick="loadFiles(\'\')">📁 根目录</a>';
// 路径部分
let currentPath = '';
for (let i = parts.length - 3; i < parts.length; i++) { // 跳过uploads前的路径
if (parts[i]) {
currentPath += '/' + parts[i];
html += '<span> / </span>';
if (i === parts.length - 1) {
html += `<span>📂 ${parts[i]}</span>`;
} else {
html += `<a href="#" onclick="loadFiles('${currentPath}')">${parts[i]}</a>`;
}
}
}
breadcrumb.innerHTML = html;
}
// 上传文件
function uploadFiles() {
const input = document.getElementById('fileInput');
const files = input.files;
if (files.length === 0) return;
showNotification('上传中...', '');
const formData = new FormData();
formData.append('action', 'upload');
formData.append('path', currentPath);
for (let i = 0; i < files.length; i++) {
formData.append('file[]', files[i]);
}
fetch('index.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification('上传成功!', 'success');
loadFiles(currentPath);
} else {
showNotification(data.message || '上传失败', 'error');
}
})
.catch(error => {
showNotification('上传失败', 'error');
});
input.value = '';
}
// 预览文件
function previewFile(path) {
if (!path && selectedFile) {
path = selectedFile.path;
}
if (!path) {
showNotification('请选择文件', 'error');
return;
}
const formData = new FormData();
formData.append('action', 'preview');
formData.append('path', path);
fetch('index.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
document.getElementById('previewContent').innerHTML = data.content;
document.getElementById('previewModal').style.display = 'block';
} else {
showNotification(data.message || '预览失败', 'error');
}
})
.catch(error => {
showNotification('预览失败', 'error');
});
}
// 下载文件
function downloadFile() {
if (selectedFile && !selectedFile.is_dir) {
window.location.href = 'download.php?file=' + encodeURIComponent(selectedFile.path);
} else {
showNotification('请选择一个文件下载', 'error');
}
}
// 删除文件
function deleteFile() {
if (!selectedFile) {
showNotification('请选择文件', 'error');
return;
}
if (!confirm(`确定要删除 "${selectedFile.name}" 吗?`)) {
return;
}
const formData = new FormData();
formData.append('action', 'delete');
formData.append('path', selectedFile.path);
fetch('index.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification(data.message, 'success');
loadFiles(currentPath);
} else {
showNotification(data.message || '删除失败', 'error');
}
})
.catch(error => {
showNotification('删除失败', 'error');
});
}
// 创建目录
function createDirectory() {
const dirName = document.getElementById('dirNameInput').value.trim();
if (!dirName) {
showNotification('请输入文件夹名称', 'error');
return;
}
const formData = new FormData();
formData.append('action', 'create_dir');
formData.append('base_path', currentPath);
formData.append('dir_name', dirName);
fetch('index.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification(data.message, 'success');
loadFiles(currentPath);
closeCreateDirModal();
document.getElementById('dirNameInput').value = '';
} else {
showNotification(data.message || '创建失败', 'error');
}
})
.catch(error => {
showNotification('创建失败', 'error');
});
}
// 显示/隐藏加载动画
function showLoading(show) {
document.getElementById('loading').style.display = show ? 'block' : 'none';
}
// 显示通知
function showNotification(message, type) {
const notification = document.getElementById('notification');
notification.textContent = message;
notification.className = 'notification ' + (type || '');
notification.style.display = 'block';
if (type) {
setTimeout(() => {
notification.style.display = 'none';
}, 3000);
}
}
// 显示右键菜单
function showContextMenu(x, y) {
const menu = document.getElementById('contextMenu');
menu.style.left = x + 'px';
menu.style.top = y + 'px';
menu.style.display = 'block';
}
// 关闭预览
function closePreview() {
document.getElementById('previewModal').style.display = 'none';
document.getElementById('previewContent').innerHTML = '';
}
// 显示新建文件夹弹窗
function showCreateDirModal() {
document.getElementById('createDirModal').style.display = 'block';
document.getElementById('dirNameInput').value = '';
document.getElementById('dirNameInput').focus();
}
// 关闭新建文件夹弹窗
function closeCreateDirModal() {
document.getElementById('createDirModal').style.display = 'none';
}
// 格式化文件大小
function formatFileSize(bytes) {
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// 格式化日期
function formatDate(timestamp) {
const date = new Date(timestamp * 1000);
const now = new Date();
const diff = now - date;
if (diff < 60000) return '刚刚';
if (diff < 3600000) return Math.floor(diff / 60000) + '分钟前';
if (diff < 86400000) return Math.floor(diff / 3600000) + '小时前';
if (diff < 604800000) return Math.floor(diff / 86400000) + '天前';
return date.toLocaleDateString('zh-CN', {
year: 'numeric',
month: '2-digit',
day: '2-digit'
});
}
// 设置拖放上传
function setupDragAndDrop() {
const container = document.querySelector('.container');
container.addEventListener('dragover', function(e) {
e.preventDefault();
this.style.borderColor = '#1a73e8';
});
container.addEventListener('dragleave', function(e) {
e.preventDefault();
this.style.borderColor = '';
});
container.addEventListener('drop', function(e) {
e.preventDefault();
this.style.borderColor = '';
const files = e.dataTransfer.files;
if (files.length > 0) {
uploadDroppedFiles(files);
}
});
}
// 上传拖放的文件
function uploadDroppedFiles(files) {
showNotification('上传中...', '');
const formData = new FormData();
formData.append('action', 'upload');
formData.append('path', currentPath);
for (let i = 0; i < files.length; i++) {
formData.append('file[]', files[i]);
}
fetch('index.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
showNotification('上传成功!', 'success');
loadFiles(currentPath);
} else {
showNotification(data.message || '上传失败', 'error');
}
})
.catch(error => {
showNotification('上传失败', 'error');
});
}
// 键盘快捷键
document.addEventListener('keydown', function(e) {
if (e.key === 'F5') {
e.preventDefault();
loadFiles(currentPath);
}
if (e.key === 'Delete' && selectedFile) {
deleteFile();
}
if (e.key === 'Escape') {
closePreview();
closeCreateDirModal();
}
});
下载文件 (download.php)
创建 download.php 文件来处理文件下载:
<?php
require_once 'config.php';
$file = isset($_GET['file']) ? $_GET['file'] : '';
if (!$file || !file_exists($file)) {
http_response_code(404);
echo '文件不存在';
exit;
}
// 安全检查 - 确保文件在uploads目录内
$real_path = realpath($file);
$upload_dir = realpath(UPLOAD_DIR);
if (strpos($real_path, $upload_dir) !== 0) {
http_response_code(403);
echo '无权访问';
exit;
}
$filename = basename($file);
$file_size = filesize($file);
// 设置下载头
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . $file_size);
ob_clean();
flush();
readfile($file);
exit;
功能特点
- 文件浏览:网格视图显示文件和文件夹,支持排序
- 文件上传:支持多文件上传和拖放上传
- 文件预览:支持图片、PDF、文本文件预览
- 文件操作:双击进入目录,右键菜单提供预览、下载、删除
- 目录管理:创建新目录,面包屑导航
- 安全功能:文件类型验证、路径安全检查
- 响应式设计:适配各种屏幕尺寸
- 友好的UI:毛玻璃效果、动画过渡、通知提示
使用