PHP脚本中文件上传与数据库更新问题的排查与修复
问题排查与修复方案
原代码存在的核心问题
- Session启动位置错误:
session_start()必须在任何输出(包括include文件)之前调用,否则会导致Session无法正常读取。 - 上传变量未初始化:
$target_file和$uploaded仅在上传成功的分支中定义,无上传或上传失败时变量未定义,触发警告并导致路径判断逻辑出错。 - 未保留原有图片路径:用户未上传新图时,代码会将
pic字段设为空,直接覆盖数据库中原有图片路径。 - 上传错误处理缺失:未检查
profile_pictures目录是否存在、权限是否足够,也未处理move_uploaded_file的失败情况。 - 重复数据库查询:代码前后两次查询用户资料,造成冗余。
- 缺少文件安全验证:未校验上传文件的类型、大小,存在安全风险且可能上传无效文件。
修复后的完整代码
<?php // 必须在任何输出前启动Session session_start(); // 启用错误报告(建议仅开发环境使用) error_reporting(E_ALL); ini_set('display_errors', 1); include 'db.php'; $nic = $_SESSION["nic"] ?? ''; $profile = null; $error_message = ''; // 仅执行一次用户资料查询 if (!empty($nic)) { $stmt = mysqli_prepare($conn, "SELECT * FROM update_profile WHERE nic = ?"); mysqli_stmt_bind_param($stmt, "s", $nic); mysqli_stmt_execute($stmt); $result = mysqli_stmt_get_result($stmt); if (mysqli_num_rows($result) > 0) { $profile = mysqli_fetch_assoc($result); } mysqli_stmt_close($stmt); } if (isset($_POST["submit"]) && !empty($nic)) { $email = trim($_POST["email"]); $school = trim($_POST["school"]); $mobile = trim($_POST["mobile"]); $stream = trim($_POST["subject_stream"]); $address = trim($_POST["address"]); $profile_pic = $_FILES["profile_pic"] ?? []; // 校验必填字段 if (empty($email) || empty($school) || empty($mobile) || empty($stream) || empty($address)) { $error_message = "所有字段均为必填项。"; } else { $uploaded = false; $target_file = ''; $existing_pic = $profile['pic'] ?? ''; $imageUrl = $existing_pic; // 处理图片上传(如果有上传) if (!empty($profile_pic) && $profile_pic['error'] == UPLOAD_ERR_OK) { // 定义上传目录 $upload_dir = __DIR__ . DIRECTORY_SEPARATOR . 'profile_pictures' . DIRECTORY_SEPARATOR; // 检查目录是否存在,不存在则创建 if (!is_dir($upload_dir)) { mkdir($upload_dir, 0755, true); } // 生成唯一文件名(避免重名) $file_ext = pathinfo($profile_pic["name"], PATHINFO_EXTENSION); $filename = $nic . '_' . uniqid() . '.' . $file_ext; $target_file = $upload_dir . $filename; // 验证文件类型(仅允许图片) $allowed_types = ['image/jpeg', 'image/png', 'image/gif']; $file_type = mime_content_type($profile_pic["tmp_name"]); if (!in_array($file_type, $allowed_types)) { $error_message = "仅允许上传JPG、PNG、GIF格式的图片。"; } elseif ($profile_pic["size"] > 2 * 1024 * 1024) { // 限制2MB以内 $error_message = "图片大小不能超过2MB。"; } else { // 执行文件上传 if (move_uploaded_file($profile_pic["tmp_name"], $target_file)) { $uploaded = true; // 构建可访问的URL路径 $imageUrl = "/vv/std/profile_pictures/" . $filename; } else { $error_message = "图片上传失败,请检查目录权限。"; } } } // 仅在无上传错误时执行数据库更新 if (empty($error_message)) { $stmt = mysqli_prepare($conn, "UPDATE update_profile SET email = ?, school = ?, mobile = ?, stream = ?, address = ?, pic = ? WHERE nic = ?"); mysqli_stmt_bind_param($stmt, "ssissss", $email, $school, $mobile, $stream, $address, $imageUrl, $nic); if (mysqli_stmt_execute($stmt)) { mysqli_stmt_close($stmt); header("Location: profile.php"); exit; } else { $error_message = "数据库更新失败: " . mysqli_stmt_error($stmt); } } } } ?>
关键修复细节说明
- Session位置调整:将
session_start()移至代码最顶部,确保Session正常读取。 - 变量初始化:提前初始化
$uploaded、$target_file等变量,避免未定义警告。 - 保留原有路径:读取数据库中已有的图片路径,用户未上传新图时直接复用。
- 目录与权限处理:自动创建缺失的上传目录,确保服务器有写入权限。
- 文件安全校验:限制图片类型和大小,防止恶意文件上传。
- 唯一文件名:使用
uniqid()生成唯一文件名,避免重名覆盖问题。 - 优化数据库操作:使用预处理语句避免SQL注入,合并重复查询减少冗余。
内容的提问来源于stack exchange,提问作者Hasala Kithmina
相关产品推荐
相关产品推荐

