PHP项目文件管理与预览

wen PHP项目 4

本文目录导读:

PHP项目文件管理与预览

  1. 完整文件管理预览系统
  2. 功能特点
  3. 使用

我将为您设计一个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()">&times;</span>
            <div id="previewContent"></div>
        </div>
    </div>
    <!-- 新建文件夹弹窗 -->
    <div id="createDirModal" class="modal">
        <div class="modal-content">
            <span class="close" onclick="closeCreateDirModal()">&times;</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;

功能特点

  1. 文件浏览:网格视图显示文件和文件夹,支持排序
  2. 文件上传:支持多文件上传和拖放上传
  3. 文件预览:支持图片、PDF、文本文件预览
  4. 文件操作:双击进入目录,右键菜单提供预览、下载、删除
  5. 目录管理:创建新目录,面包屑导航
  6. 安全功能:文件类型验证、路径安全检查
  7. 响应式设计:适配各种屏幕尺寸
  8. 友好的UI:毛玻璃效果、动画过渡、通知提示

使用

抱歉,评论功能暂时关闭!