完整的PHP文件上传组件的实现,包含基本的上传功能、安全检查和错误处理。
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>
安全增强建议
- 文件重命名:使用随机文件名而不是用户提供的文件名,防止目录遍历攻击
- 文件类型检查:不要依赖文件扩展名,可以使用
finfo_file()
检查实际MIME类型 - 目录权限:确保上传目录不可执行,防止上传的PHP文件被执行
- 病毒扫描:对于生产环境,应考虑集成病毒扫描功能
- 文件内容验证:对于图片,可以使用
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;
}
这个实现提供了基本的文件上传功能,可以根据具体需求进行扩展和定制。