Skip to content

13.3 上传验证(大小、类型、重命名)

文件上传验证是确保上传文件安全和符合要求的重要步骤。本章节将介绍如何验证上传文件的大小、类型,并实现文件重命名功能。

验证文件大小

1. 服务器端验证

php
// 限制文件大小为 5MB
$maxFileSize = 5 * 1024 * 1024; // 5MB

if ($_FILES['fileToUpload']['size'] > $maxFileSize) {
    $error = "文件太大,最大允许 5MB";
}

2. 客户端验证

html
<!-- HTML5 文件大小验证 -->
<input type="file" name="fileToUpload" accept="image/*" onchange="validateFileSize(this);">

<script>
function validateFileSize(input) {
    const maxSize = 5 * 1024 * 1024; // 5MB
    if (input.files && input.files[0]) {
        if (input.files[0].size > maxSize) {
            alert('文件太大,最大允许 5MB');
            input.value = '';
        }
    }
}
</script>

3. 配置文件限制

php.ini 文件中设置:

ini
upload_max_filesize = 5M
post_max_size = 10M

验证文件类型

1. 通过 MIME 类型验证

php
// 允许的 MIME 类型
$allowedMimeTypes = [
    'image/jpeg',
    'image/png',
    'image/gif',
    'image/webp'
];

if (!in_array($_FILES['fileToUpload']['type'], $allowedMimeTypes)) {
    $error = "只允许上传图片文件(JPG、PNG、GIF、WebP)";
}

2. 通过文件扩展名验证

php
// 允许的文件扩展名
$allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];

$fileExtension = strtolower(pathinfo($_FILES['fileToUpload']['name'], PATHINFO_EXTENSION));

if (!in_array($fileExtension, $allowedExtensions)) {
    $error = "只允许上传图片文件(JPG、PNG、GIF、WebP)";
}

3. 验证是否为真实图片

php
// 检查是否为真实图片
if (getimagesize($_FILES['fileToUpload']['tmp_name']) === false) {
    $error = "请上传真实的图片文件";
}

4. 客户端文件类型验证

html
<!-- 只允许图片文件 -->
<input type="file" name="fileToUpload" accept="image/*">

<!-- 只允许特定类型 -->
<input type="file" name="fileToUpload" accept=".jpg,.jpeg,.png,.gif">

文件重命名

1. 使用唯一 ID 重命名

php
$targetDir = "uploads/";
$fileExtension = strtolower(pathinfo($_FILES['fileToUpload']['name'], PATHINFO_EXTENSION));
$newFileName = uniqid() . '.' . $fileExtension;
$targetFile = $targetDir . $newFileName;

if (move_uploaded_file($_FILES['fileToUpload']['tmp_name'], $targetFile)) {
    echo "文件上传成功,新文件名: " . $newFileName;
}

2. 使用时间戳重命名

php
$targetDir = "uploads/";
$fileExtension = strtolower(pathinfo($_FILES['fileToUpload']['name'], PATHINFO_EXTENSION));
$newFileName = time() . '_' . mt_rand(1000, 9999) . '.' . $fileExtension;
$targetFile = $targetDir . $newFileName;

if (move_uploaded_file($_FILES['fileToUpload']['tmp_name'], $targetFile)) {
    echo "文件上传成功,新文件名: " . $newFileName;
}

3. 保留原始文件名但避免冲突

php
$targetDir = "uploads/";
$originalFileName = basename($_FILES['fileToUpload']['name']);
$fileExtension = strtolower(pathinfo($originalFileName, PATHINFO_EXTENSION));
$fileNameWithoutExt = pathinfo($originalFileName, PATHINFO_FILENAME);

$targetFile = $targetDir . $originalFileName;
$counter = 1;

// 避免文件名冲突
while (file_exists($targetFile)) {
    $newFileName = $fileNameWithoutExt . '_' . $counter . '.' . $fileExtension;
    $targetFile = $targetDir . $newFileName;
    $counter++;
}

if (move_uploaded_file($_FILES['fileToUpload']['tmp_name'], $targetFile)) {
    echo "文件上传成功,文件���: " . basename($targetFile);
}

完整的上传验证示例

php
<?php
$targetDir = "uploads/";
$maxFileSize = 5 * 1024 * 1024; // 5MB
$allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
$errors = [];

// 确保上传目录存在
if (!is_dir($targetDir)) {
    mkdir($targetDir, 0755, true);
}

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['fileToUpload'])) {
    $file = $_FILES['fileToUpload'];
    
    // 检查是否有错误
    if ($file['error'] !== 0) {
        switch ($file['error']) {
            case UPLOAD_ERR_INI_SIZE:
                $errors[] = '文件大小超过了 php.ini 限制';
                break;
            case UPLOAD_ERR_FORM_SIZE:
                $errors[] = '文件大小超过了表单限制';
                break;
            case UPLOAD_ERR_PARTIAL:
                $errors[] = '文件只有部分被上传';
                break;
            case UPLOAD_ERR_NO_FILE:
                $errors[] = '没有文件被上传';
                break;
            case UPLOAD_ERR_NO_TMP_DIR:
                $errors[] = '找不到临时文件夹';
                break;
            case UPLOAD_ERR_CANT_WRITE:
                $errors[] = '文件写入失败';
                break;
            case UPLOAD_ERR_EXTENSION:
                $errors[] = '文件上传被 PHP 扩展中断';
                break;
            default:
                $errors[] = '上传失败,未知错误';
        }
    } else {
        // 检查文件大小
        if ($file['size'] > $maxFileSize) {
            $errors[] = "文件太大,最大允许 5MB";
        }
        
        // 检查文件扩展名
        $fileExtension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
        if (!in_array($fileExtension, $allowedExtensions)) {
            $errors[] = "只允许上传图片文件(JPG、PNG、GIF、WebP)";
        }
        
        // 检查是否为真实图片
        if (getimagesize($file['tmp_name']) === false) {
            $errors[] = "请上传真实的图片文件";
        }
        
        // 处理上传
        if (empty($errors)) {
            // 重命名文件
            $newFileName = uniqid() . '.' . $fileExtension;
            $targetFile = $targetDir . $newFileName;
            
            if (move_uploaded_file($file['tmp_name'], $targetFile)) {
                $success = "文件上传成功!<br>文件名: " . $newFileName . "<br>大小: " . round($file['size'] / 1024, 2) . " KB";
            } else {
                $errors[] = "上传失败,请重试";
            }
        }
    }
}
?>

<!DOCTYPE html>
<html>
<head>
    <title>文件上传验证</title>
    <style>
        body { font-family: Arial, sans-serif; max-width: 500px; margin: 0 auto; padding: 20px; }
        h1 { text-align: center; color: #333; }
        .form-group { margin: 15px 0; }
        label { display: block; margin-bottom: 5px; font-weight: bold; }
        input[type="file"] { padding: 10px; border: 1px solid #ddd; border-radius: 4px; width: 100%; box-sizing: border-box; }
        input[type="submit"] { padding: 10px 20px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; }
        .message { padding: 10px; margin: 10px 0; border-radius: 4px; }
        .success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
        .error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
    </style>
</head>
<body>
    <h1>文件上传验证</h1>
    
    <?php if (!empty($errors)): ?>
        <div class="message error">
            <ul>
                <?php foreach ($errors as $error): ?>
                    <li><?php echo $error; ?></li>
                <?php endforeach; ?>
            </ul>
        </div>
    <?php endif; ?>
    
    <?php if (isset($success)): ?>
        <div class="message success">
            <?php echo $success; ?>
        </div>
    <?php endif; ?>
    
    <form action="" method="post" enctype="multipart/form-data">
        <div class="form-group">
            <label>选择图片: <input type="file" name="fileToUpload" accept="image/*"></label>
        </div>
        <input type="submit" value="上传文件" name="submit">
    </form>
</body>
</html>

多文件上传验证

php
<?php
$targetDir = "uploads/";
$maxFileSize = 5 * 1024 * 1024; // 5MB
$allowedExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
$errors = [];
$successFiles = [];

// 确保上传目录存在
if (!is_dir($targetDir)) {
    mkdir($targetDir, 0755, true);
}

if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['files'])) {
    $files = $_FILES['files'];
    $fileCount = count($files['name']);
    
    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]
        ];
        
        // 检查是否有错误
        if ($file['error'] !== 0) {
            $errors[] = "文件 " . $file['name'] . " 上传失败: 错误代码 " . $file['error'];
            continue;
        }
        
        // 检查文件大小
        if ($file['size'] > $maxFileSize) {
            $errors[] = "文件 " . $file['name'] . " 太大,最大允许 5MB";
            continue;
        }
        
        // 检查文件扩展名
        $fileExtension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
        if (!in_array($fileExtension, $allowedExtensions)) {
            $errors[] = "文件 " . $file['name'] . " 类型不允许,只允许图片文件";
            continue;
        }
        
        // 检查是否为真实图片
        if (getimagesize($file['tmp_name']) === false) {
            $errors[] = "文件 " . $file['name'] . " 不是真实的图片文件";
            continue;
        }
        
        // 重命名文件
        $newFileName = uniqid() . '.' . $fileExtension;
        $targetFile = $targetDir . $newFileName;
        
        if (move_uploaded_file($file['tmp_name'], $targetFile)) {
            $successFiles[] = [
                'original' => $file['name'],
                'new' => $newFileName,
                'size' => round($file['size'] / 1024, 2)
            ];
        } else {
            $errors[] = "文件 " . $file['name'] . " 上传失败";
        }
    }
}
?>

<!DOCTYPE html>
<html>
<head>
    <title>多文件上传验证</title>
    <style>
        body { font-family: Arial, sans-serif; max-width: 500px; margin: 0 auto; padding: 20px; }
        h1 { text-align: center; color: #333; }
        .form-group { margin: 15px 0; }
        label { display: block; margin-bottom: 5px; font-weight: bold; }
        input[type="file"] { padding: 10px; border: 1px solid #ddd; border-radius: 4px; width: 100%; box-sizing: border-box; }
        input[type="submit"] { padding: 10px 20px; background-color: #4CAF50; color: white; border: none; border-radius: 4px; cursor: pointer; }
        .message { padding: 10px; margin: 10px 0; border-radius: 4px; }
        .success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
        .error { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
        .file-list { margin: 10px 0; }
        .file-item { padding: 5px; border-bottom: 1px solid #ddd; }
    </style>
</head>
<body>
    <h1>多文件上传验证</h1>
    
    <?php if (!empty($errors)): ?>
        <div class="message error">
            <ul>
                <?php foreach ($errors as $error): ?>
                    <li><?php echo $error; ?></li>
                <?php endforeach; ?>
            </ul>
        </div>
    <?php endif; ?>
    
    <?php if (!empty($successFiles)): ?>
        <div class="message success">
            <p>上传成功的文件:</p>
            <div class="file-list">
                <?php foreach ($successFiles as $file): ?>
                    <div class="file-item">
                        原始文件名: <?php echo $file['original']; ?><br>
                        新文件名: <?php echo $file['new']; ?><br>
                        大小: <?php echo $file['size']; ?> KB
                    </div>
                <?php endforeach; ?>
            </div>
        </div>
    <?php endif; ?>
    
    <form action="" method="post" enctype="multipart/form-data">
        <div class="form-group">
            <label>选择图片: <input type="file" name="files[]" multiple accept="image/*"></label>
        </div>
        <input type="submit" value="上传文件" name="submit">
    </form>
</body>
</html>

注意事项

  1. 安全性

    • 不要信任用户上传的文件,始终进行验证
    • 不要将上传的文件直接存储在可执行目录
    • 考虑使用安全的文件存储路径
  2. 性能

    • 限制上传文件的大小,避免服务器资源耗尽
    • 对于大文件,考虑使用分片上传
  3. 用户体验

    • 提供清晰的错误提示
    • 显示上传进度(对于大文件)
    • 提供上传成功的反馈
  4. 兼容性

    • 不同浏览器对文件上传的支持不同
    • 考虑移动设备的文件上传体验

练习

  1. 实现一个完整的文件上传验证系统
  2. 支持多文件上传验证
  3. 实现文件类型、大小的双重验证
  4. 实现文件重命名功能,避免文件名冲突
  5. 显示上传进度和成功/失败信息

© 2026 编程马·菜鸟教程 版权所有