本文目录导读:

我将为您设计一个PHP文件上传共享存储系统,这个系统支持多用户文件上传、浏览和下载功能。
数据库结构
-- 创建文件表
CREATE TABLE IF NOT EXISTS files (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
original_name VARCHAR(255) NOT NULL,
stored_name VARCHAR(255) NOT NULL,
file_path VARCHAR(500) NOT NULL,
file_size INT NOT NULL,
mime_type VARCHAR(100),
category VARCHAR(50),
description TEXT,
downloads_count INT DEFAULT 0,
is_public TINYINT(1) DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_user_id (user_id),
INDEX idx_category (category),
INDEX idx_created (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- 用户表(简化版,实际项目中通常已有)
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
email VARCHAR(100) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
上传配置文件
<?php
// config.php
define('UPLOAD_DIR', __DIR__ . '/uploads/'); // 上传目录
define('ALLOWED_EXTENSIONS', ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'zip', 'rar']);
define('MAX_FILE_SIZE', 50 * 1024 * 1024); // 50MB
define('MAX_FILES_PER_UPLOAD', 10);
define('DATE_FORMAT', 'Y-m-d H:i:s');
// 创建上传目录(如果不存在)
if (!file_exists(UPLOAD_DIR)) {
mkdir(UPLOAD_DIR, 0755, true);
mkdir(UPLOAD_DIR . 'images/', 0755, true);
mkdir(UPLOAD_DIR . 'documents/', 0755, true);
mkdir(UPLOAD_DIR . 'archives/', 0755, true);
}
文件上传类
<?php
// FileUploader.php
class FileUploader {
private $uploadDir;
private $allowedExts;
private $maxSize;
public function __construct() {
$this->uploadDir = UPLOAD_DIR;
$this->allowedExts = ALLOWED_EXTENSIONS;
$this->maxSize = MAX_FILE_SIZE;
}
/**
* 上传单个文件
*/
public function uploadFile($file, $userId, $category = 'general', $isPublic = true) {
$result = [
'success' => false,
'message' => '',
'file' => null
];
if ($file['error'] !== UPLOAD_ERR_OK) {
$result['message'] = '上传失败,错误码: ' . $file['error'];
return $result;
}
// 检查文件大小
if ($file['size'] > $this->maxSize) {
$result['message'] = '文件大小超过限制';
return $result;
}
// 检查文件类型
$fileInfo = pathinfo($file['name']);
$extension = strtolower($fileInfo['extension']);
if (!in_array($extension, $this->allowedExts)) {
$result['message'] = '不支持的文件类型';
return $result;
}
// 生成存储文件名
$storedName = $this->generateStoredName($fileInfo['filename'], $extension);
// 根据文件类型分配到子目录
$subDir = $this->getSubDirectory($extension);
$targetPath = $this->uploadDir . $subDir;
// 确保目录存在
if (!file_exists($targetPath)) {
mkdir($targetPath, 0755, true);
}
$targetFile = $targetPath . '/' . $storedName;
// 移动文件
if (move_uploaded_file($file['tmp_name'], $targetFile)) {
// 保存文件信息到数据库
$fileData = [
'user_id' => $userId,
'original_name' => $file['name'],
'stored_name' => $storedName,
'file_path' => $subDir . '/' . $storedName,
'file_size' => $file['size'],
'mime_type' => $file['type'],
'category' => $category,
'is_public' => $isPublic ? 1 : 0
];
$fileId = $this->saveFileInfo($fileData);
if ($fileId) {
$fileData['id'] = $fileId;
$result['success'] = true;
$result['file'] = $fileData;
$result['message'] = '文件上传成功';
} else {
unlink($targetFile); // 删除已上传的文件
$result['message'] = '保存文件信息失败';
}
} else {
$result['message'] = '文件移动失败';
}
return $result;
}
/**
* 批量上传文件
*/
public function uploadMultipleFiles($files, $userId, $category = 'general') {
$results = [];
$count = count($files['name']);
for ($i = 0; $i < $count; $i++) {
$file = [
'name' => $files['name'][$i],
'type' => $files['type'][$i],
'tmp_name' => $files['tmp_name'][$i],
'error' => $files['error'][$i],
'size' => $files['size'][$i]
];
$results[] = $this->uploadFile($file, $userId, $category);
}
return $results;
}
/**
* 生成存储文件名
*/
private function generateStoredName($originalName, $extension) {
$unique = uniqid() . '_' . substr(md5(uniqid(mt_rand(), true)), 0, 8);
return $unique . '.' . $extension;
}
/**
* 根据文件类型返回子目录
*/
private function getSubDirectory($extension) {
$imageExts = ['jpg', 'jpeg', 'png', 'gif'];
$docExts = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt'];
$archiveExts = ['zip', 'rar'];
if (in_array($extension, $imageExts)) {
return 'images';
} elseif (in_array($extension, $docExts)) {
return 'documents';
} elseif (in_array($extension, $archiveExts)) {
return 'archives';
} else {
return 'others';
}
}
/**
* 保存文件信息到数据库
*/
private function saveFileInfo($fileData) {
$db = Database::getInstance()->getConnection();
$sql = "INSERT INTO files (user_id, original_name, stored_name, file_path, file_size, mime_type, category, is_public)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
$stmt = $db->prepare($sql);
$stmt->bind_param("isssissi",
$fileData['user_id'],
$fileData['original_name'],
$fileData['stored_name'],
$fileData['file_path'],
$fileData['file_size'],
$fileData['mime_type'],
$fileData['category'],
$fileData['is_public']
);
if ($stmt->execute()) {
return $db->insert_id;
}
return false;
}
}
数据库连接类
<?php
// Database.php
class Database {
private static $instance = null;
private $connection;
private $host = 'localhost';
private $username = 'root';
private $password = '';
private $database = 'file_sharing';
private function __construct() {
$this->connection = new mysqli($this->host, $this->username, $this->password, $this->database);
if ($this->connection->connect_error) {
die("连接失败: " . $this->connection->connect_error);
}
$this->connection->set_charset("utf8mb4");
}
public static function getInstance() {
if (self::$instance === null) {
self::$instance = new Database();
}
return self::$instance;
}
public function getConnection() {
return $this->connection;
}
}
文件管理类
<?php
// FileManager.php
class FileManager {
private $db;
public function __construct() {
$this->db = Database::getInstance()->getConnection();
}
/**
* 获取文件列表
*/
public function getFiles($userId = null, $category = null, $search = '', $page = 1, $perPage = 20) {
$offset = ($page - 1) * $perPage;
$where = [];
$params = [];
$types = "";
if ($userId !== null) {
$where[] = "user_id = ?";
$params[] = $userId;
$types .= "i";
}
if ($category && $category !== 'all') {
$where[] = "category = ?";
$params[] = $category;
$types .= "s";
}
if (!empty($search)) {
$where[] = "original_name LIKE ?";
$params[] = "%$search%";
$types .= "s";
}
// 只显示公开文件或自己的文件
$where[] = "(is_public = 1 OR user_id = ?)";
$params[] = $_SESSION['user_id'] ?? 0;
$types .= "i";
$whereSQL = $where ? "WHERE " . implode(" AND ", $where) : "";
$sql = "SELECT * FROM files $whereSQL ORDER BY created_at DESC LIMIT ? OFFSET ?";
$params[] = $perPage;
$params[] = $offset;
$types .= "ii";
$stmt = $this->db->prepare($sql);
$stmt->bind_param($types, ...$params);
$stmt->execute();
$result = $stmt->get_result();
$files = $result->fetch_all(MYSQLI_ASSOC);
// 获取总数
$countSQL = "SELECT COUNT(*) as total FROM files $whereSQL";
$stmt = $this->db->prepare($countSQL);
$countParams = array_slice($params, 0, count($params) - 2);
$countTypes = substr($types, 0, -2);
if ($countTypes) {
$stmt->bind_param($countTypes, ...$countParams);
}
$stmt->execute();
$countResult = $stmt->get_result()->fetch_assoc();
return [
'files' => $files,
'total' => $countResult['total'],
'page' => $page,
'perPage' => $perPage,
'totalPages' => ceil($countResult['total'] / $perPage)
];
}
/**
* 获取单个文件信息
*/
public function getFileById($fileId) {
$sql = "SELECT * FROM files WHERE id = ?";
$stmt = $this->db->prepare($sql);
$stmt->bind_param("i", $fileId);
$stmt->execute();
return $stmt->get_result()->fetch_assoc();
}
/**
* 下载文件
*/
public function downloadFile($fileId) {
$file = $this->getFileById($fileId);
if (!$file) {
return ['success' => false, 'message' => '文件不存在'];
}
$filePath = UPLOAD_DIR . $file['file_path'];
if (!file_exists($filePath)) {
return ['success' => false, 'message' => '文件物理文件不存在'];
}
// 更新下载次数
$this->incrementDownloadCount($fileId);
// 配置下载响应
header('Content-Description: File Transfer');
header('Content-Type: ' . $file['mime_type']);
header('Content-Disposition: attachment; filename="' . $file['original_name'] . '"');
header('Content-Length: ' . filesize($filePath));
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Expires: 0');
readfile($filePath);
return ['success' => true];
}
/**
* 删除文件
*/
public function deleteFile($fileId, $userId) {
$file = $this->getFileById($fileId);
if (!$file) {
return ['success' => false, 'message' => '文件不存在'];
}
// 检查权限(只有文件所有者可以删除)
if ($file['user_id'] != $userId) {
return ['success' => false, 'message' => '没有删除权限'];
}
// 删除物理文件
$filePath = UPLOAD_DIR . $file['file_path'];
if (file_exists($filePath)) {
unlink($filePath);
}
// 删除数据库记录
$sql = "DELETE FROM files WHERE id = ? AND user_id = ?";
$stmt = $this->db->prepare($sql);
$stmt->bind_param("ii", $fileId, $userId);
if ($stmt->execute()) {
return ['success' => true, 'message' => '文件删除成功'];
}
return ['success' => false, 'message' => '删除失败'];
}
/**
* 更新文件信息
*/
public function updateFileInfo($fileId, $userId, $data) {
$allowedFields = ['category', 'description', 'is_public'];
$updates = [];
$params = [];
$types = "";
foreach ($data as $key => $value) {
if (in_array($key, $allowedFields)) {
$updates[] = "$key = ?";
$params[] = $value;
$types .= "s";
}
}
if (empty($updates)) {
return ['success' => false, 'message' => '没有要更新的字段'];
}
$params[] = $fileId;
$params[] = $userId;
$types .= "ii";
$sql = "UPDATE files SET " . implode(", ", $updates) . " WHERE id = ? AND user_id = ?";
$stmt = $this->db->prepare($sql);
$stmt->bind_param($types, ...$params);
if ($stmt->execute()) {
return ['success' => true, 'message' => '更新成功'];
}
return ['success' => false, 'message' => '更新失败'];
}
/**
* 增加下载次数
*/
private function incrementDownloadCount($fileId) {
$sql = "UPDATE files SET downloads_count = downloads_count + 1 WHERE id = ?";
$stmt = $this->db->prepare($sql);
$stmt->bind_param("i", $fileId);
$stmt->execute();
}
/**
* 获取文件分类统计
*/
public function getCategoryStats() {
$sql = "SELECT category, COUNT(*) as count FROM files GROUP BY category";
$result = $this->db->query($sql);
return $result->fetch_all(MYSQLI_ASSOC);
}
}
上传页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">文件上传中心</title>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
background-color: #f5f5f5;
}
.container {
max-width: 1200px;
margin: 0 auto;
background-color: white;
padding: 20px;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
.upload-form {
background: #f9f9f9;
padding: 20px;
border-radius: 5px;
margin-bottom: 20px;
}
.form-group {
margin-bottom: 15px;
}
.form-group label {
display: block;
margin-bottom: 5px;
font-weight: bold;
}
.form-group input,
.form-group select,
.form-group textarea {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
}
.btn {
background-color: #4CAF50;
color: white;
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 16px;
}
.btn:hover {
background-color: #45a049;
}
.file-list {
margin-top: 20px;
}
.file-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
gap: 15px;
margin-top: 20px;
}
.file-card {
background-color: white;
border: 1px solid #ddd;
padding: 15px;
border-radius: 5px;
}
.file-card h4 {
margin: 0 0 10px 0;
color: #333;
}
.file-card .meta {
color: #666;
font-size: 12px;
}
.file-card .actions {
margin-top: 10px;
}
.file-card .actions a {
margin-right: 10px;
text-decoration: none;
color: #2196F3;
}
.file-card .actions a:hover {
text-decoration: underline;
}
.success {
background-color: #d4edda;
color: #155724;
padding: 10px;
border-radius: 4px;
margin: 10px 0;
}
.error {
background-color: #f8d7da;
color: #721c24;
padding: 10px;
border-radius: 4px;
margin: 10px 0;
}
</style>
</head>
<body>
<div class="container">
<h1>文件上传共享系统</h1>
<?php if (!isset($_SESSION['user_id'])): ?>
<div class="error">请先登录</div>
<?php else: ?>
<div class="upload-form">
<h2>上传文件</h2>
<form action="upload_handler.php" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="files">选择文件(可多选)</label>
<input type="file" id="files" name="files[]" multiple required>
<input type="hidden" name="MAX_FILE_SIZE" value="<?php echo MAX_FILE_SIZE; ?>">
</div>
<div class="form-group">
<label for="category">文件分类</label>
<select id="category" name="category">
<option value="general">综合</option>
<option value="images">图片</option>
<option value="documents">文档</option>
<option value="archives">压缩包</option>
<option value="videos">视频</option>
<option value="audio">音频</option>
<option value="other">其他</option>
</select>
</div>
<div class="form-group">
<label for="description">文件描述</label>
<textarea id="description" name="description" rows="3"></textarea>
</div>
<div class="form-group">
<label>
<input type="checkbox" name="is_public" value="1" checked>
公开共享
</label>
</div>
<button type="submit" class="btn">上传文件</button>
</form>
</div>
<div class="file-list">
<h2>文件列表</h2>
<div class="file-grid">
<?php
$fileManager = new FileManager();
$files = $fileManager->getFiles(null, null, '', 1, 100);
foreach ($files['files'] as $file):
?>
<div class="file-card">
<h4><?php echo htmlspecialchars($file['original_name']); ?></h4>
<div class="meta">
<p>大小: <?php echo formatFileSize($file['file_size']); ?></p>
<p>类型: <?php echo htmlspecialchars($file['mime_type']); ?></p>
<p>分类: <?php echo htmlspecialchars($file['category']); ?></p>
<p>上传时间: <?php echo date('Y-m-d H:i', strtotime($file['created_at'])); ?></p>
<p>下载次数: <?php echo $file['downloads_count']; ?></p>
</div>
<div class="actions">
<a href="download.php?id=<?php echo $file['id']; ?>">下载</a>
<?php if ($_SESSION['user_id'] == $file['user_id']): ?>
<a href="delete.php?id=<?php echo $file['id']; ?>" onclick="return confirm('确定要删除这个文件吗?')">删除</a>
<?php endif; ?>
</div>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>
</div>
</body>
</html>
上传处理脚本
<?php
// upload_handler.php
session_start();
require_once 'config.php';
require_once 'Database.php';
require_once 'FileUploader.php';
// 检查用户是否登录
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
// 验证上传请求
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['files'])) {
$uploader = new FileUploader();
$category = $_POST['category'] ?? 'general';
$isPublic = isset($_POST['is_public']) ? true : false;
$results = $uploader->uploadMultipleFiles(
$_FILES['files'],
$_SESSION['user_id'],
$category,
$isPublic
);
// 处理结果
$successCount = 0;
$errorMessages = [];
foreach ($results as $result) {
if ($result['success']) {
$successCount++;
} else {
$errorMessages[] = $result['message'];
}
}
// 保存上传结果到会话
$_SESSION['upload_messages'] = [
'success_count' => $successCount,
'errors' => $errorMessages
];
header('Location: index.php');
exit;
}
下载处理脚本
<?php
// download.php
session_start();
require_once 'config.php';
require_once 'Database.php';
require_once 'FileManager.php';
if (!isset($_SESSION['user_id'])) {
header('Location: login.php');
exit;
}
$fileId = $_GET['id'] ?? null;
if ($fileId) {
$fileManager = new FileManager();
$result = $fileManager->downloadFile($fileId);
// 如果返回了结果,说明文件不存在
if (!$result['success']) {
$_SESSION['error_message'] = $result['message'];
header('Location: index.php');
exit;
}
} else {
header('Location: index.php');
exit;
}
辅助函数
<?php
// helpers.php
/**
* 格式化文件大小
*/
function formatFileSize($size) {
if ($size >= 1073741824) {
return round($size / 1073741824, 2) . ' GB';
} elseif ($size >= 1048576) {
return round($size / 1048576, 2) . ' MB';
} elseif ($size >= 1024) {
return round($size / 1024, 2) . ' KB';
} else {
return $size . ' bytes';
}
}
/**
* 检查文件路径安全性
*/
function isSafePath($path) {
$realPath = realpath(UPLOAD_DIR . $path);
if (!$realPath) {
return false;
}
$uploadDir = realpath(UPLOAD_DIR);
return strpos($realPath, $uploadDir) === 0;
}
/**
* 生成安全的文件名
*/
function generateSafeFileName($name) {
// 移除危险字符
$name = preg_replace('/[^\w\.\-]/', '', $name);
// 限制长度
return substr($name, 0, 255);
}
页面提示功能
<?php
// 在index.php的合适位置添加
if (isset($_SESSION['upload_messages'])) {
$messages = $_SESSION['upload_messages'];
if ($messages['success_count'] > 0) {
echo '<div class="success">';
echo $messages['success_count'] . ' 个文件上传成功';
echo '</div>';
}
foreach ($messages['errors'] as $error) {
echo '<div class="error">' . htmlspecialchars($error) . '</div>';
}
unset($_SESSION['upload_messages']);
}
if (isset($_SESSION['error_message'])) {
echo '<div class="error">' . htmlspecialchars($_SESSION['error_message']) . '</div>';
unset($_SESSION['error_message']);
}
安全注意事项
-
文件类型验证:始终验证文件的实际MIME类型,而不仅仅是文件扩展名。
-
文件名安全:不要直接使用用户提供的文件名,生成唯一的新文件名。
-
路径安全:确保文件路径不包含../等路径穿越字符。
-
访问控制:确保只有文件所有者或公开文件可以被访问。
-
文件大小限制:在服务器端PHP配置和应用程序层面都设置上传大小限制。
-
使用HTTPS:在生产环境中使用HTTPS保护文件传输。
这个系统提供了一个完整的文件上传共享解决方案,支持多文件上传、分类管理、搜索、下载统计等功能,您可以根据实际需求进行扩展和修改。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播