PHP图片压缩失效的根本原因是move_uploaded_file()覆盖了已压缩文件,正确做法是先移动再用GD处理并覆盖,需校验文件类型、释放内存、合理设置质量参数并优先缩放尺寸。

上传后直接覆盖原文件,而不是先 move_uploaded_file() 再用 GD 重读重写——否则容易被原始大图覆盖掉已压缩结果。
压缩必须在 move 后、保存前完成
很多人把压缩逻辑写在 move_uploaded_file() 之前,或者压根没意识到:GD 的 imagejpeg() 等函数写入的是内存图像,不是临时文件本身。如果先 move_uploaded_file($tmp, $dst),再 imagecreatefromjpeg($dst) → imagejpeg($img, $dst),看似覆盖了,但若中间没加判断或路径错,就白忙活。
- 正确顺序是:
move_uploaded_file()成功后,立即用 GD 加载刚存好的文件,处理完再用imagejpeg($img, $dst, $quality)覆盖它 - 务必检查
file_exists($dst)和exif_imagetype($dst),避免对非图文件调用 GD 函数导致崩溃 - 别省略
imagedestroy($img),尤其批量处理时,不释放会快速耗尽内存
JPEG/PNG 质量参数不能瞎设
imagejpeg() 第三个参数不是“越高越好”,imagepng() 第三个参数也不是“越小越省”。设错会导致体积不降反升,或画质崩坏。
- JPEG 推荐质量值:70–80;60 以下边缘易出块状噪点,100 不等于无损,反而体积暴涨 2–3 倍
- PNG 压缩等级是 0–9,推荐用 6;设 9 虽最省空间,但 CPU 时间翻倍,对高并发上传不友好
- 对 PNG 透明图,记得加
imagesavealpha($img, true),否则透明区域会被填黑
别跳过尺寸缩放,光调质量没用
一张 4000×3000 的图,就算 imagejpeg($img, $dst, 60),体积仍可能超 800KB。浏览器还得下载整张大图,首屏卡顿照旧。
立即学习“PHP免费学习笔记(深入)”;
- 必须先用
imagecopyresampled()缩到业务需要的尺寸(比如头像 200×200、Banner 1200×400),再压缩 - 禁用
imagecopyresized(),它插值粗糙,缩完图发虚 - 缩放前用
getimagesize()拿宽高,按比例算新尺寸,避免拉伸变形
WebP 不是万能,但值得条件启用
WebP 确实比 JPEG 小 25%–35%,但硬切会炸掉 Safari 13 以下、IE 所有版本的用户图片。
- 只在
$_SERVER['HTTP_ACCEPT']包含image/webp时才生成 WebP,并保留原图 JPEG/PNG 作为 fallback - 输出 WebP 前检查
gd_info()['webp support'] === true,否则imagewebp()直接报错 - 别用 WebP 替换原图文件名,应另存为
$name.webp,前端用<picture>标签切换
真正卡住人的从来不是几行 GD 代码,而是缩放与压缩的先后顺序、质量值的业务适配、以及 WebP 的降级兜底——这些地方一漏,优化就归零。



















