本文目录导读:

我来详细介绍PHP中实现文件上传功能的完整步骤。
基础HTML表单
首先创建上传表单页面:
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">文件上传</title>
</head>
<body>
<form action="upload.php" method="POST" enctype="multipart/form-data">
选择文件:
<input type="file" name="fileToUpload" id="fileToUpload">
<input type="submit" value="上传文件" name="submit">
</form>
</body>
</html>
关键点:
method="POST":必须使用POST方法enctype="multipart/form-data":必须设置此编码类型
基础PHP上传处理
<?php
// upload.php
$targetDir = "uploads/";
$targetFile = $targetDir . basename($_FILES["fileToUpload"]["name"]);
$uploadOk = 1;
$imageFileType = strtolower(pathinfo($targetFile, PATHINFO_EXTENSION));
// 检查文件是否已存在
if (file_exists($targetFile)) {
echo "抱歉,文件已存在。";
$uploadOk = 0;
}
// 限制文件大小(5MB)
if ($_FILES["fileToUpload"]["size"] > 5000000) {
echo "抱歉,文件太大。";
$uploadOk = 0;
}
// 限制文件类型
$allowedTypes = ["jpg", "jpeg", "png", "gif", "pdf", "doc", "docx"];
if (!in_array($imageFileType, $allowedTypes)) {
echo "抱歉,只允许上传 JPG, JPEG, PNG, GIF, PDF, DOC 文件。";
$uploadOk = 0;
}
// 检查是否有错误
if ($uploadOk == 0) {
echo "抱歉,文件上传失败。";
} else {
// 尝试上传文件
if (move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $targetFile)) {
echo "文件 ". htmlspecialchars(basename($_FILES["fileToUpload"]["name"])). " 上传成功。";
} else {
echo "抱歉,上传文件时发生错误。";
}
}
?>
完整的安全上传类
<?php
class FileUploader {
private $targetDir;
private $allowedTypes;
private $maxSize;
private $overwrite;
public function __construct($targetDir, $maxSize = 5000000, $overwrite = false) {
$this->targetDir = $targetDir;
$this->maxSize = $maxSize;
$this->overwrite = $overwrite;
$this->allowedTypes = ['jpg', 'jpeg', 'png', 'gif', 'pdf', 'doc', 'docx', 'txt', 'zip'];
// 创建上传目录
if (!file_exists($targetDir)) {
mkdir($targetDir, 0755, true);
}
}
/**
* 上传单个文件
*/
public function upload($file) {
$result = ['success' => false, 'message' => '', 'filename' => ''];
// 检查是否有上传错误
if ($file['error'] !== UPLOAD_ERR_OK) {
$result['message'] = $this->getErrorMessage($file['error']);
return $result;
}
// 生成安全文件名
$extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
$newFilename = $this->generateUniqueFilename($extension);
$targetPath = $this->targetDir . $newFilename;
// 验证文件类型
if (!$this->isAllowedType($extension)) {
$result['message'] = "不允许的文件类型: $extension";
return $result;
}
// 验证文件大小
if ($file['size'] > $this->maxSize) {
$result['message'] = "文件超过大小限制: " . ($this->maxSize / 1000000) . "MB";
return $result;
}
// 验证文件内容(MIME类型检查)
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mimeType = finfo_file($finfo, $file['tmp_name']);
finfo_close($finfo);
if (!$this->isAllowedMimeType($mimeType)) {
$result['message'] = "无效的文件内容";
return $result;
}
// 执行上传
if (move_uploaded_file($file['tmp_name'], $targetPath)) {
$result['success'] = true;
$result['message'] = "文件上传成功";
$result['filename'] = $newFilename;
} else {
$result['message'] = "文件上传失败";
}
return $result;
}
/**
* 生成唯一文件名
*/
private function generateUniqueFilename($extension) {
$filename = uniqid() . '_' . time() . '.' . $extension;
// 如果不覆盖且文件存在,添加随机数
if (!$this->overwrite && file_exists($this->targetDir . $filename)) {
$filename = uniqid() . '_' . time() . '_' . rand(1000, 9999) . '.' . $extension;
}
return $filename;
}
/**
* 检查文件类型
*/
private function isAllowedType($extension) {
return in_array($extension, $this->allowedTypes);
}
/**
* 检查MIME类型
*/
private function isAllowedMimeType($mimeType) {
$allowedMimes = [
'image/jpeg',
'image/png',
'image/gif',
'application/pdf',
'application/msword',
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'text/plain',
'application/zip'
];
return in_array($mimeType, $allowedMimes);
}
/**
* 获取上传错误信息
*/
private function getErrorMessage($errorCode) {
switch ($errorCode) {
case UPLOAD_ERR_INI_SIZE:
return "文件超过PHP.ini配置的大小限制";
case UPLOAD_ERR_FORM_SIZE:
return "文件超过表单指定的大小限制";
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 "扩展阻止了文件上传";
default:
return "未知错误";
}
}
}
?>
使用上传类
<?php
// safe_upload.php
require_once 'FileUploader.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['fileToUpload'])) {
$uploader = new FileUploader('uploads/', 5000000, false); // 5MB限制,不覆盖
$result = $uploader->upload($_FILES['fileToUpload']);
if ($result['success']) {
echo "上传成功!文件名: " . $result['filename'];
} else {
echo "上传失败: " . $result['message'];
}
}
?>
多文件上传
<!-- 多文件上传表单 -->
<form action="multi_upload.php" method="POST" enctype="multipart/form-data">
选择文件(可多选):
<input type="file" name="files[]" multiple>
<input type="submit" value="上传">
</form>
<?php
// multi_upload.php
require_once 'FileUploader.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['files'])) {
$uploader = new FileUploader('uploads/');
$files = $_FILES['files'];
$fileCount = count($files['name']);
$successCount = 0;
for ($i = 0; $i < $fileCount; $i++) {
$file = [
'name' => $files['name'][$i],
'type' => $files['type'][$i],
'tmp_name' => $files['tmp_name'][$i],
'error' => $files['error'][$i],
'size' => $files['size'][$i]
];
$result = $uploader->upload($file);
if ($result['success']) {
$successCount++;
}
}
echo "成功上传 $successCount 个文件";
}
?>
安全配置
在 php.ini 中配置上传相关参数:
; 开启文件上传 file_uploads = On ; 临时文件目录 upload_tmp_dir = /tmp ; 单个文件最大大小(默认2MB) upload_max_filesize = 10M ; POST数据最大大小 post_max_size = 12M ; 允许最大并发上传文件数 max_file_uploads = 20 ; 脚本执行时间 max_execution_time = 300 ; 内存限制 memory_limit = 128M
安全注意事项
<?php
// 安全增强措施
// 1. 验证用户权限(示例)
if (!isset($_SESSION['user_id'])) {
die("请先登录");
}
// 2. 限制上传目录(防止目录遍历)
$allowedDirs = ['user_uploads', 'profile_images'];
$uploadDir = 'user_uploads/';
if (!in_array($uploadDir, $allowedDirs)) {
die("无效的上传目录");
}
// 3. 扫描病毒(使用ClamAV示例)
function scanFile($filePath) {
$clamscan = '/usr/bin/clamscan';
$cmd = "$clamscan --no-summary $filePath 2>&1";
$output = shell_exec($cmd);
return strpos($output, 'OK') !== false;
}
// 4. 检查文件是否包含恶意代码
function checkForMaliciousCode($filePath) {
$content = file_get_contents($filePath);
$suspicious = ['<?php', '<?', '<script', 'eval(', 'exec(', 'system('];
foreach ($suspicious as $pattern) {
if (stripos($content, $pattern) !== false) {
return true;
}
}
return false;
}
// 5. 设置安全的文件权限
chmod($targetFile, 0644);
?>
文件上传功能的实现要点:
- 表单设置:正确设置
enctype和method - 文件验证:检查类型、大小、MIME类型
- 安全文件名:使用唯一文件名,防止覆盖
- 权限控制:验证用户身份和权限
- 错误处理:处理好各种错误情况
- 安全扫描:检查文件是否包含恶意代码
建议在实际应用中结合框架(如Laravel、Symfony)的文件上传组件,它们提供了更多安全特性和便利功能。