完整的PHP文件上传组件的实现,包含基本的上传功能、安全检查和错误处理。 - Winmax Music 

PHP 文件上传组件实现

下面是一个完整的PHP文件上传组件的实现,包含基本的上传功能、安全检查和错误处理。

基础文件上传代码

<?php
class FileUploader {
    private $uploadDir = 'uploads/';  // 默认上传目录
    private $allowedTypes = [];      // 允许的文件类型
    private $maxSize = 2097152;       // 默认最大2MB
    private $errors = [];             // 错误信息
    
    /**
     * 构造函数
     * @param string $uploadDir 上传目录
     * @param array $allowedTypes 允许的文件类型
     * @param int $maxSize 最大文件大小(字节)
     */
    public function __construct($uploadDir = '', $allowedTypes = [], $maxSize = 0) {
        if (!empty($uploadDir)) {
            $this->uploadDir = rtrim($uploadDir, '/') . '/';
        }
        
        if (!empty($allowedTypes)) {
            $this->allowedTypes = $allowedTypes;
        }
        
        if ($maxSize > 0) {
            $this->maxSize = $maxSize;
        }
        
        // 自动创建上传目录
        if (!file_exists($this->uploadDir)) {
            mkdir($this->uploadDir, 0755, true);
        }
    }
    
    /**
     * 上传文件
     * @param array $file $_FILES数组中的元素
     * @return string|false 成功返回文件名,失败返回false
     */
    public function upload($file) {
        // 重置错误信息
        $this->errors = [];
        
        // 检查文件上传是否成功
        if ($file['error'] !== UPLOAD_ERR_OK) {
            $this->errors[] = $this->getUploadError($file['error']);
            return false;
        }
        
        // 检查文件大小
        if ($file['size'] > $this->maxSize) {
            $this->errors[] = "文件太大,最大允许 " . $this->formatSize($this->maxSize);
            return false;
        }
        
        // 检查文件类型
        $fileExt = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
        if (!empty($this->allowedTypes) {
            if (!in_array($fileExt, $this->allowedTypes)) {
                $this->errors[] = "不允许的文件类型,只允许: " . implode(', ', $this->allowedTypes);
                return false;
            }
        }
        
        // 生成唯一文件名
        $filename = uniqid() . '.' . $fileExt;
        $destination = $this->uploadDir . $filename;
        
        // 移动文件到目标位置
        if (move_uploaded_file($file['tmp_name'], $destination)) {
            return $filename;
        } else {
            $this->errors[] = "文件移动失败";
            return false;
        }
    }
    
    /**
     * 获取错误信息
     * @return array 错误信息数组
     */
    public function getErrors() {
        return $this->errors;
    }
    
    /**
     * 获取上传错误描述
     * @param int $errorCode 错误代码
     * @return string 错误描述
     */
    private function getUploadError($errorCode) {
        switch ($errorCode) {
            case UPLOAD_ERR_INI_SIZE:
                return '上传的文件超过了php.ini中upload_max_filesize限制';
            case UPLOAD_ERR_FORM_SIZE:
                return '上传文件的大小超过了HTML表单中MAX_FILE_SIZE选项指定的值';
            case UPLOAD_ERR_PARTIAL:
                return '文件只有部分被上传';
            case UPLOAD_ERR_NO_FILE:
                return '没有文件被上传';
            case UPLOAD_ERR_NO_TMP_DIR:
                return '找不到临时文件夹';
            case UPLOAD_ERR_CANT_WRITE:
                return '文件写入失败';
            case UPLOAD_ERR_EXTENSION:
                return '上传被PHP扩展阻止';
            default:
                return '未知上传错误';
        }
    }
    
    /**
     * 格式化文件大小
     * @param int $bytes 字节数
     * @return string 格式化后的字符串
     */
    private function formatSize($bytes) {
        if ($bytes >= 1073741824) {
            return number_format($bytes / 1073741824, 2) . ' GB';
        } elseif ($bytes >= 1048576) {
            return number_format($bytes / 1048576, 2) . ' MB';
        } elseif ($bytes >= 1024) {
            return number_format($bytes / 1024, 2) . ' KB';
        } else {
            return $bytes . ' bytes';
        }
    }
}

使用示例

<?php
// 引入文件上传类
require_once 'FileUploader.php';

// 配置
$uploadDir = 'uploads/images/';  // 上传目录
$allowedTypes = ['jpg', 'jpeg', 'png', 'gif'];  // 允许的文件类型
$maxSize = 5 * 1024 * 1024;  // 5MB

// 创建上传器实例
$uploader = new FileUploader($uploadDir, $allowedTypes, $maxSize);

// 处理表单提交
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) {
    $filename = $uploader->upload($_FILES['file']);
    
    if ($filename) {
        echo "文件上传成功: " . htmlspecialchars($filename);
    } else {
        echo "文件上传失败:";
        foreach ($uploader->getErrors() as $error) {
            echo "<br> - " . htmlspecialchars($error);
        }
    }
}
?>

<!DOCTYPE html>
<html>
<head>
    <title>文件上传示例</title>
</head>
<body>
    <form action="" method="post" enctype="multipart/form-data">
        <label for="file">选择文件:</label>
        <input type="file" name="file" id="file">
        <input type="submit" value="上传">
    </form>
</body>
</html>

安全增强建议

  1. 文件重命名:使用随机文件名而不是用户提供的文件名,防止目录遍历攻击
  2. 文件类型检查:不要依赖文件扩展名,可以使用finfo_file()检查实际MIME类型
  3. 目录权限:确保上传目录不可执行,防止上传的PHP文件被执行
  4. 病毒扫描:对于生产环境,应考虑集成病毒扫描功能
  5. 文件内容验证:对于图片,可以使用getimagesize()验证确实是图片文件

增强的文件类型检查

可以在upload()方法中添加以下代码:

// 使用finfo检查实际文件类型
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);

// 允许的MIME类型映射
$allowedMimes = [
    'jpg' => 'image/jpeg',
    'jpeg' => 'image/jpeg',
    'png' => 'image/png',
    'gif' => 'image/gif'
];

if (!in_array($mime, $allowedMimes)) {
    $this->errors[] = "不允许的文件类型";
    return false;
}

这个实现提供了基本的文件上传功能,可以根据具体需求进行扩展和定制。

标签: php, code



没事发点牢骚,评论几句?!Nothing to complain about, comment a few words.