You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP图片上传校验生效但无效文件仍被上传问题求助

博客图片上传校验失效,无效文件仍被上传

我开发了一个博客应用,支持上传图片并将文件名保存到数据库,已经添加了校验规则:禁止上传大于500KB的文件,仅允许jpg、png、webp、gif格式。现在的问题是,当检测到文件过大或格式无效时,页面会跳回发布表单并显示错误提示,但无效文件还是会被上传到服务器。我是PHP初学者,找不到问题根源,附上相关代码请帮忙指点。

发布表单 - makepost.php

<!-- HEADER.PHP -->
<?php require "templates/header.php" ?>
  <main class="container p-4 bg-light mt-3" style="width: 1000px">
    <!-- createpost.inc.php - Will process the data from this form-->
    <form action="includes/makepost.inc.php" method="POST" enctype="multipart/form-data">
      <h2>Create Post</h2>

      <!-- Error Message -->
      <?php
        // VALIDATION: Check that Error Message Type exists in GET superglobal
        if(isset($_GET['error'])){
          // (1) Empty fields validation 
          if($_GET['error'] == "emptyfields"){
            $errorMsg = "Please fill in all fields";

          // (2) Internal server error 
          } else if ($_GET['error'] == "sqlerror") {
            $errorMsg = "An internal server error has occurred - please try again later";

          // (3) Banner Image file name already exists 
          } else if ($_GET['error'] == "file-name-match") {
            $errorMsg = "Sorry, this banner image file already exists. Please rename your file.";

          // (4) Banner Image file size is to large 
          } else if ($_GET['error'] == "file-size-to-large") {
            $errorMsg = "Sorry, your banner image file is too large. Please reduce our image file size.";

          // (5) Is the uploaded image using a valid file type
          } else if ($_GET['error'] == "invalid-file-type") {
            $errorMsg = "Sorry, only JPG, JPEG, PNG, GIF & WEBP files are allowed.";

          // (6) Is the upload an actual image file
          } else if ($_GET['error'] == "file-is-not-an-image-file") {
            $errorMsg = "Sorry, your file is not an image.";

          } else if ($_GET['error'] == "unknown-or-general-error") {
            $errorMsg = "Sorry, there was an error uploading your file.";
          }
          
          // (8) Dynamic Error Alert based on Variable Value 
          echo '<div class="alert alert-danger" role="alert">' . $errorMsg . '</div>';

        }
      ?>
      <!-- 1. Article Titile -->
      <div class="mb-3">
        <label for="title" class="form-label">Title</label>
        <input type="text" class="form-control" name="title" placeholder="Title" value="">
      </div>  

      <!-- 2. Upload Image File -->
      <div class="mb-3">
        <label for="fileToUpload" class="form-label">Banner Image</label>
        <input type="file" class="form-control" name="fileToUpload">
      </div>

      <!-- 3. Article Extract -->
      <div class="mb-3">
        <label for="extract" class="form-label">Article Extract</label>
        <textarea id="extract-textarea" class="form-control" name="extract" rows="3"></textarea>
      </div>

      <!-- 3. Article Text -->
      <div class="mb-3">
        <label for="article" class="form-label">Article Text</label>
        <textarea id="article-textarea" class="form-control" name="article" rows="3"></textarea>
      </div>

      <!-- 4. Submit Button -->
      <button type="submit" name="post-submit" class="btn btn-primary w-100">Post</button>
    </form>
  </main>
<!-- FOOTER.PHP -->
<?php require "templates/footer.php" ?>

处理发布请求的包含文件 - makepost.inc.php

<?php
  // 01) Start Session.
  session_start();

  // 02) Load the upload directory config.
  require 'config.inc.php';

  // 03) Set the upload parameters.
  $target_file = $directory . basename($_FILES["fileToUpload"]["name"]);
  $uploadOk = 1;
  $imageFileType = strtolower(pathinfo($target_file,PATHINFO_EXTENSION));

  // 04) Check user clicked submit button from makepost form + user is logged in.
  if(isset($_POST['post-submit']) && isset($_SESSION['userId']) && move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)){
    
    // 05) Load the database connection settings file.
    require 'connect.inc.php';

    // 06) Collect andstore POST data
    $title = $_POST['title']; // Post Title.
    $imageURL = $_FILES['fileToUpload']['name']; // Image URL - Add option for image upload.
    $extract  = $_POST['extract']; // Post Extract.
    $article  = $_POST['article']; // Article Text.
    $postdate  = date("Y-m-d"); // Get Current Date for Post Date.
    $author  = $_SESSION['userUid']; // Use 'userUid' in $_SESSION Varible for Author Name.

    // 07) VALIDATION: Check if any fields are empty.
    if (empty($title ) || empty($imageURL) || empty($extract) || empty($article) || empty($postdate) || empty($author)) {
    
      // 08) ERROR: Redirect + error via GET.
      header("Location: ../makepost.php?error=emptyfields");
      exit();

      // 09) Checks if the image files size exceeds file size limit of 500KB.
      } else if ($_FILES["fileToUpload"]["size"] > 500000) {
      header("Location: ../makepost.php?error=file-size-to-large"); 
      $uploadOk = 0;
      exit();

      // 10) Checks if the image is a an excepted file type.     
      } else if ($imageFileType != "jpg" && $imageFileType != "png" && $imageFileType != "jpeg"
      && $imageFileType != "gif" && $imageFileType != "webp" ) {
      header("Location: ../makepost.php?error=invalid-file-type");
      $uploadOk = 0;
      exit();

    // 11) Save the post to the database using prepared statements.
    } else {
      // 12) Declare Template SQL with ? Placeholders to save values to table.
      $sql = "INSERT INTO posts VALUES (NULL, ?, ?, ?, ?, ?, ?)"; 

      // 13) Init SQL statement.
      $statement = mysqli_stmt_init($conn);

      // 14) Prepare + send statement to database to check for errors.
      if(!mysqli_stmt_prepare($statement, $sql))
      {
        // 15) ERROR: Something wrong when preparing the SQL.
        header("Location: ../makepost.php?error=sqlerror"); 
        exit();
      } else {
        // 16) SUCCESS: Bind our user data with statement + escape strings.
        mysqli_stmt_bind_param($statement, "ssssss", $title, $imageURL, $extract,  $article, $postdate, $author);

        // 17) Execute the SQL Statement with user data.
        mysqli_stmt_execute($statement);

        // 18) SUCCESS: Post is saved to "posts" table - redirect with success message.
        header("Location: ../index.php?post=success"); 
        exit();
      }
    }
  // 19) Restrict Access to Script Page.
  } else {
    header("Location: ../index.php");
    exit();
  }
?>

我尝试在else if语句末尾添加exit();但没有效果。


问题根源与修复方案

核心问题

你在执行所有校验逻辑之前,已经调用了move_uploaded_file()把文件上传到服务器了。也就是说,不管文件是否符合规则,只要用户提交了表单且登录状态有效,文件就已经被移动到目标目录,之后的校验只是跳回页面提示错误,但文件已经上传完成了。

修复步骤

  1. 调整逻辑顺序:先做所有校验(空字段、文件大小、文件类型),确认文件完全符合规则后,再执行move_uploaded_file()和数据库写入操作。
  2. 优化条件判断:把move_uploaded_file()从初始的if条件中移除,放到校验通过后的逻辑里。

修改后的makepost.inc.php核心代码片段

<?php
session_start();
require 'config.inc.php';

$target_file = $directory . basename($_FILES["fileToUpload"]["name"]);
$uploadOk = 1;
$imageFileType = strtolower(pathinfo($target_file,PATHINFO_EXTENSION));

// 先检查提交状态和登录状态,不先上传文件
if(isset($_POST['post-submit']) && isset($_SESSION['userId'])){
    require 'connect.inc.php';

    $title = $_POST['title'];
    $imageURL = $_FILES['fileToUpload']['name'];
    $extract  = $_POST['extract'];
    $article  = $_POST['article'];
    $postdate  = date("Y-m-d");
    $author  = $_SESSION['userUid'];

    // 1. 空字段校验
    if (empty($title ) || empty($imageURL) || empty($extract) || empty($article) || empty($postdate) || empty($author)) {
        header("Location: ../makepost.php?error=emptyfields");
        exit();
    }
    // 2. 文件大小校验
    else if ($_FILES["fileToUpload"]["size"] > 500000) {
        header("Location: ../makepost.php?error=file-size-to-large"); 
        exit();
    }
    // 3. 文件类型校验
    else if (!in_array($imageFileType, ["jpg", "png", "jpeg", "gif", "webp"])) {
        header("Location: ../makepost.php?error=invalid-file-type");
        exit();
    }
    // 所有校验通过,再上传文件并写入数据库
    else {
        // 执行文件上传
        if(move_uploaded_file($_FILES["fileToUpload"]["tmp_name"], $target_file)){
            // 数据库写入逻辑
            $sql = "INSERT INTO posts VALUES (NULL, ?, ?, ?, ?, ?, ?)"; 
            $statement = mysqli_stmt_init($conn);
            if(!mysqli_stmt_prepare($statement, $sql)){
                header("Location: ../makepost.php?error=sqlerror"); 
                exit();
            } else {
                mysqli_stmt_bind_param($statement, "ssssss", $title, $imageURL, $extract,  $article, $postdate, $author);
                mysqli_stmt_execute($statement);
                header("Location: ../index.php?post=success"); 
                exit();
            }
        } else {
            // 文件上传失败的情况
            header("Location: ../makepost.php?error=unknown-or-general-error");
            exit();
        }
    }
} else {
    header("Location: ../index.php");
    exit();
}
?>

额外优化建议

  • 可以添加文件是否为真实图片的校验(比如用getimagesize()),防止用户上传伪装成图片的恶意文件。
  • 处理文件名重复的情况:可以给文件名添加时间戳或随机字符串,避免覆盖已有文件,而不是让用户手动重命名。

内容的提问来源于stack exchange,提问作者Lachlan Williams

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 16:46:17