PHP项目超额存储如何拦截上传文件

wen PHP项目 29

本文目录导读:

PHP项目超额存储如何拦截上传文件

  1. 服务器层配额控制
  2. 程序层实时监控
  3. 高级方案:流式限制
  4. 监控与报警系统
  5. 前端拦截(辅助方案)
  6. 推荐方案组合

在PHP项目中拦截超额存储(超出磁盘配额或存储限制)的上传文件,可以通过以下几种方案实现:

服务器层配额控制

PHP配置文件限制

// php.ini 配置
upload_max_filesize = 50M
post_max_size = 55M
max_file_uploads = 10

磁盘配额管理(Linux)

# 创建配额限制
sudo setquota -u www-data 500M 600M /var/www/html
# 检查配额
sudo repquota -a

程序层实时监控

检查磁盘空间

<?php
class FileStorageGuard {
    private $maxStorage;
    private $uploadDir;
    public function __construct($maxStorage = 1073741824) { // 1GB
        $this->maxStorage = $maxStorage;
        $this->uploadDir = '/path/to/uploads';
    }
    /**
     * 检查剩余空间
     */
    public function checkAvailableSpace($fileSize) {
        $freeSpace = disk_free_space($this->uploadDir);
        $usedSpace = $this->getUsedSpace();
        $remainingSpace = $this->maxStorage - $usedSpace;
        if ($remainingSpace <= 0) {
            throw new Exception('存储空间已满');
        }
        if ($fileSize > $remainingSpace) {
            throw new Exception('文件过大,剩余空间不足');
        }
        return $remainingSpace;
    }
    /**
     * 计算已用空间
     */
    private function getUsedSpace() {
        $iterator = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($this->uploadDir, 
                RecursiveDirectoryIterator::SKIP_DOTS)
        );
        $totalSize = 0;
        foreach ($iterator as $file) {
            if ($file->isFile()) {
                $totalSize += $file->getSize();
            }
        }
        return $totalSize;
    }
    /**
     * 处理上传文件
     */
    public function handleUpload($file, $destination) {
        // 检查文件大小
        if ($file['size'] > $this->checkAvailableSpace($file['size'])) {
            return false;
        }
        // 移动文件
        return move_uploaded_file($file['tmp_name'], $destination);
    }
}
// 使用示例
$guard = new FileStorageGuard(5 * 1024 * 1024 * 1024); // 5GB
try {
    $guard->handleUpload($_FILES['file'], '/path/to/destination/file.pdf');
} catch (Exception $e) {
    echo '上传失败: ' . $e->getMessage();
}

数据库存储配额

<?php
class QuotaManager {
    private $db;
    private $userId;
    public function __construct($userId) {
        $this->db = new PDO('mysql:host=localhost;dbname=file_manager', 'user', 'pass');
        $this->userId = $userId;
    }
    /**
     * 获取用户配额
     */
    public function getUserQuota() {
        $stmt = $this->db->prepare("
            SELECT quota_limit, 
                   (SELECT COALESCE(SUM(file_size), 0) FROM files WHERE user_id = ?) as used
            FROM users WHERE id = ?
        ");
        $stmt->execute([$this->userId, $this->userId]);
        return $stmt->fetch(PDO::FETCH_ASSOC);
    }
    /**
     * 检查上传是否允许
     */
    public function canUpload($fileSize) {
        $quota = $this->getUserQuota();
        $available = $quota['quota_limit'] - $quota['used'];
        if ($fileSize > $available) {
            throw new Exception("配额不足,剩余空间: " . $this->formatBytes($available));
        }
        return true;
    }
    /**
     * 记录上传
     */
    public function recordUpload($filePath, $fileSize) {
        $stmt = $this->db->prepare("
            INSERT INTO files (user_id, file_path, file_size, uploaded_at) 
            VALUES (?, ?, ?, NOW())
        ");
        return $stmt->execute([$this->userId, $filePath, $fileSize]);
    }
    private function formatBytes($bytes) {
        $units = ['B', 'KB', 'MB', 'GB', 'TB'];
        $i = 0;
        while ($bytes >= 1024 && $i < count($units) - 1) {
            $bytes /= 1024;
            $i++;
        }
        return round($bytes, 2) . ' ' . $units[$i];
    }
}

高级方案:流式限制

使用流包装器限制写入

<?php
class LimitedWriteStream {
    private $maxBytes;
    private $written = 0;
    private $stream;
    public function __construct($stream, $maxBytes) {
        $this->stream = $stream;
        $this->maxBytes = $maxBytes;
    }
    public function write($data) {
        $remaining = $this->maxBytes - $this->written;
        if ($remaining <= 0) {
            throw new Exception('写入限制已达到');
        }
        $writeLen = min(strlen($data), $remaining);
        $written = fwrite($this->stream, substr($data, 0, $writeLen));
        $this->written += $written;
        return $written;
    }
    public function __destruct() {
        fclose($this->stream);
    }
}
// 使用示例
function uploadWithLimit($sourceFile, $destPath, $maxSize) {
    $source = fopen($sourceFile, 'rb');
    $dest = fopen($destPath, 'wb');
    $limited = new LimitedWriteStream($dest, $maxSize);
    while (!feof($source)) {
        $data = fread($source, 8192);
        $limited->write($data);
    }
    fclose($source);
}

监控与报警系统

实时磁盘监控

<?php
class DiskMonitor {
    private $threshold = 90; // 90% 告警
    public function checkDiskUsage($partition) {
        $total = disk_total_space($partition);
        $free = disk_free_space($partition);
        $usedPercent = (($total - $free) / $total) * 100;
        if ($usedPercent > $this->threshold) {
            $this->sendAlert("磁盘使用率: {$usedPercent}%");
            return false;
        }
        // 自动清理策略
        if ($usedPercent > 80) {
            $this->autoCleanup($partition);
        }
        return true;
    }
    private function autoCleanup($dir) {
        $files = glob($dir . '/*.tmp'); // 清理临时文件
        foreach ($files as $file) {
            if (filemtime($file) < time() - 86400) { // 超过24小时
                unlink($file);
            }
        }
    }
    private function sendAlert($message) {
        // 发送邮件、短信或Webhook通知
        mail('admin@example.com', '磁盘空间告警', $message);
    }
}

前端拦截(辅助方案)

JavaScript预检查

function checkFileSize(file, maxSizeMB = 10) {
    const maxSizeBytes = maxSizeMB * 1024 * 1024;
    if (file.size > maxSizeBytes) {
        alert(`文件大小不能超过 ${maxSizeMB}MB`);
        return false;
    }
    // 可选:检查剩余空间(需要API支持)
    checkServerStorage().then(available => {
        if (file.size > available) {
            alert('服务器存储空间不足');
            return false;
        }
    });
    return true;
}
// HTML表单
<form onsubmit="return checkFileSize(this.file.files[0], 50)">
    <input type="file" name="file" required>
    <button type="submit">上传</button>
</form>

推荐方案组合

  1. 初级项目:使用PHP配置 + 程序层检查
  2. 中级项目:数据库配额管理 + 磁盘监控
  3. 高级项目:流式限制 + 分布式存储 + 实时监控

关键点

  • 始终在服务端进行最终验证
  • 实现多层防御(前端、应用层、系统层)
  • 定期清理过期文件
  • 设置合理的缓冲阈值(通常预留10-20%空间)

选择哪种方案取决于项目的具体需求和预算,对于大多数PHP项目,组合使用数据库配额管理程序层检查就足够应对常见场景。

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