优先用 domdocument 解析 html 提取 img 标签 src,再校验 base64 图片;若用正则需兼容空格、引号、大小写 mime 并清理换行;保存路径须在 public/ 下,mkdir 要加 true 参数;扩展名仅从白名单 mime 映射,不信任客户端输入。

preg_match 匹配 base64 图片头失败:常见 pattern 写法误区
直接用 preg_match('/data:image\/(\w+);base64,/', $str, $m) 极易漏匹配,尤其在 ThinkPHP8.0 的富文本内容中:src 属性可能带空格、换行、单引号、大小写 MIME(如 image/JPEG),甚至被 HTML 注释或 JS 字符串干扰。
更稳妥的写法是:
- 优先用
DOMDocument解析 HTML,遍历所有<img>标签,检查$img->getAttribute('src')是否以data:image/开头 —— 避免误匹配文本或 CSS 中的 base64 - 若必须用正则,至少改用:
preg_match('/src\s*=\s*["\']\s*data:image\/([a-zA-Z+.\-]+);base64,([^"\']+)/i', $html, $m) -
base64字符串本身可能含\n或\r\n,base64_decode()会直接返回false,务必先str_replace(["\n", "\r"], '', $m[2])
file_put_contents 写入失败:路径、权限与目录创建陷阱
ThinkPHP8.0 默认静态资源由 public/ 目录提供服务,任何写入都必须落在可被 Web 服务器直接访问的路径下,比如 public/uploads/。写进 runtime/ 或 app/ 目录,即使成功也无法通过 URL 加载。
实操要点:
- 保存路径应为:
$dir = ROOT_PATH . 'public' . DS . 'uploads' . DS . date('Ymd') . DS; - 创建目录时必须加第三个参数
true:mkdir($dir, 0755, true),否则多级路径(如uploads/20260506)会报错 - 文件名别用原始 base64 哈希,推荐:
$filename = uniqid() . '.' . strtolower($ext);($ext来自 MIME 映射,不是用户传的后缀) - 务必检查
file_put_contents($full_path, $binary)返回值,失败时调error_get_last()查具体原因(常见:目录不可写、磁盘满、SELinux 限制)
替换 HTML 中的 src 为相对 URL:DOM 操作比 str_replace 安全得多
富文本里可能有多个 data:image/xxx;base64,,也可能出现在 background、srcset 或 JS 字符串中。str_replace() 粗暴替换会污染非 <img> 场景,导致前端解析异常。
正确做法是基于 DOM 替换:
- 用
$dom = new DOMDocument(); $dom->loadHTML($html, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);加载 - 遍历所有
<img>,对每个匹配的src,生成绝对路径(如/uploads/20260506/abc123.png),再调$img->setAttribute('src', $new_url) - 最后用
$dom->saveHTML()输出,确保引号、转义、结构完整 - 注意:ThinkPHP8.0 路由默认不拦截以
/uploads/开头的请求,但 Nginx/Apache 配置仍需确认该路径未被 deny
扩展名合法性校验:仅从 MIME 类型映射,不信任客户端传参
用户可能伪造 data:image/php;base64,... 或传 type=jpg 但实际是 PHP shell。ThinkPHP8.0 不自带 MIME 白名单,必须手动控制。
关键处理逻辑:
- 从正则捕获的 MIME(如
$m[1])映射到合法扩展名:$mimeMap = ['jpeg'=>'jpg', 'pjpeg'=>'jpg', 'png'=>'png', 'gif'=>'gif', 'webp'=>'webp']; - 若
!isset($mimeMap[strtolower($m[1])]),直接拒绝,不进入解码流程 - 解码后建议加二进制头校验(如用
finfo_open(FILEINFO_MIME_TYPE)确认真实类型),避免扩展名欺骗 - 文件大小也需限制,例如
if (strlen($base64) * 0.75 > 5 * 1024 * 1024)(base64 编码后体积约膨胀 33%)
最易被忽略的是:MIME 到扩展名的映射必须小写统一,且不能依赖 $base64_image_content 中任意位置出现的字符串 —— 它只应来自正则捕获组或 DOMElement::getAttribute() 的原始值。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











