图片应存服务器目录或对象存储,数据库仅存路径;须校验$_files['error']、is_uploaded_file()、getimagesize()及类型;配置upload_max_filesize与post_max_size,并检查$_files['size'];用pdo预处理插入,且必须判断move_uploaded_file()返回值。

图片上传后存哪儿?别直接扔数据库
MySQL 存图片二进制(BLOB)看似省事,实际是坑:拖慢查询、备份膨胀、CDN 无法加速、PHP 内存容易爆。真实项目里,99% 的情况应该只存路径,文件落地到服务器目录或对象存储。
实操建议:
- 上传时用
move_uploaded_file()把临时文件移到/uploads/2024/06/这类带年月的子目录下,避免单目录文件过多 - 生成唯一文件名,比如
md5(uniqid() . $_FILES['img']['name']) . '.jpg',防止重名覆盖 - 数据库字段设为
VARCHAR(255),存相对路径如uploads/2024/06/abc123.jpg,不是绝对路径 - 确保
uploads/目录有写权限(chmod 755或775),且 Web 服务器用户(如www-data)能写入
$_FILES 数组里哪些键必须检查?
很多人只看 $_FILES['img']['tmp_name'] 就直接挪文件,结果上传失败不报错、空文件入库、甚至被传恶意脚本。关键要逐项校验。
必须检查的项:
立即学习“PHP免费学习笔记(深入)”;
-
$_FILES['img']['error'] === UPLOAD_ERR_OK—— 不是0就别往下走,否则可能是超限、无文件、临时目录不可写等 -
is_uploaded_file($_FILES['img']['tmp_name'])—— 防止伪造tmp_name路径进行 LFI -
getimagesize($_FILES['img']['tmp_name'])返回非false—— 确认真是图片,不是把.php改后缀骗过表单 -
in_array($_FILES['img']['type'], ['image/jpeg', 'image/png', 'image/gif'])—— 类型只是参考,必须配合getimagesize用
怎么防止用户上传超大图片压垮服务器?
光靠前端 max_file_size 隐藏域或 JS 校验毫无意义,HTTP 请求里可以任意篡改。真正起作用的是 PHP 配置和代码层双重限制。
两处必须同步设:
- PHP 配置:
upload_max_filesize = 8M和post_max_size = 10M(后者要 ≥ 前者,否则整个 POST 被截断) - 代码里检查:
$_FILES['img']['size'] ,单位是字节,别写成 <code>8 * 1000 * 1000 - 如果用 Nginx,还要加
client_max_body_size 10M;,否则请求根本到不了 PHP - 超限时返回明确错误,比如
if ($_FILES['img']['error'] === UPLOAD_ERR_INI_SIZE) echo '文件不能超过 8MB';
插入数据库前,路径怎么拼才安全?
有人把用户传的原始文件名直接拼进 SQL,或者不做转义就插进 INSERT,轻则乱码,重则 SQL 注入。路径本身虽非用户可控,但文件名部分仍需清洗。
安全做法:
- 用
basename()剥离路径,防../../etc/passwd类攻击 - 用正则过滤掉非字母、数字、下划线、短横线:
preg_replace('/[^a-zA-Z0-9_.\-]/', '', $filename) - 插入数据库必须用预处理语句:
$stmt = $pdo->prepare("INSERT INTO photos (path, title) VALUES (?, ?)"); $stmt->execute([$safe_path, $title]); - 别用
mysql_real_escape_string(已废弃),也别手动拼 SQL 字符串
路径拼接和数据库写入之间,最容易漏掉的是对 move_uploaded_file() 返回值的判断 —— 它失败时不会抛异常,只返回 false,接着插个空路径进库,前端一访问就是 404。这地方没日志、没提示,查起来特别费时间。











