推荐用DOMDocument精准替换img的src,避免正则误伤;小文件可用preg_replace_callback但需处理引号与空格;写回时应原子操作并验证。

用 file_get_contents 读取并替换 HTML 文件中的 <img> 的 src
直接操作文件内容最常用也最稳妥的方式是读入字符串,用正则或 DOM 处理,再写回。别用 fopen + 逐行替换——<img> 标签可能跨行、带换行/缩进/注释,正则也得考虑属性顺序(src 不一定在开头)。
推荐用 DOMDocument,它能准确解析标签结构,避免误伤文本中类似 src= 的非标签内容:
$doc = new DOMDocument();
libxml_use_internal_errors(true); // 忽略 HTML5 中常见警告(如缺少 <html>)
$doc->loadHTML(file_get_contents('page.html'), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
libxml_clear_errors();
foreach ($doc->getElementsByTagName('img') as $img) {
$oldSrc = $img->getAttribute('src');
if (str_starts_with($oldSrc, 'https://old-domain.com/')) {
$newSrc = str_replace('https://old-domain.com/', 'https://new-cdn.com/', $oldSrc);
$img->setAttribute('src', $newSrc);
}
}
// 输出时去掉自动添加的 <html><body> 包裹
echo $doc->saveHTML($doc->documentElement);
注意:saveHTML() 默认会补全文档结构,加 $doc->documentElement 参数只输出 body 内容(对纯片段更安全)。
用 preg_replace 快速替换但必须避开常见陷阱
如果只是简单替换且确认 HTML 结构干净(比如模板生成的静态页),可以用正则,但必须处理好引号、空格和属性顺序:
立即学习“PHP免费学习笔记(深入)”;
-
src="..."和src='...'都要匹配,不能只写双引号 - 属性间可能有多个空格或换行,
s*要写够 - 别用
.*贪婪匹配,否则一个正则可能跨多个<img>标签
安全一点的写法:
$html = file_get_contents('page.html');
$html = preg_replace_callback(
'/<img s+[^ alt="用PHP替换文件中的图片链接地址" >]*srcs*=s*(['"])(.*?)\1[^>]*>/i',
function ($matches) {
$quote = $matches[1];
$oldSrc = $matches[2];
$newSrc = str_replace('https://old-domain.com/', 'https://new-cdn.com/', $oldSrc);
return str_replace($matches[2], $newSrc, $matches[0]);
},
$html
);
这个正则捕获引号类型,再用 \1 确保闭合一致,比硬编码单双引号更鲁棒。
写回文件前务必检查路径权限和编码
替换完内容后,用 file_put_contents 写回,但容易出错的地方有:
- 目标文件路径没写对,比如漏了
./或拼错目录名,结果写到别的地方甚至根目录 - 原文件是 UTF-8 with BOM,DOM 解析后可能丢 BOM,导致某些老浏览器渲染异常(少见但存在)
- 没加
LOCK_EX,并发写入时可能覆盖彼此:file_put_contents('page.html', $html, LOCK_EX)
建议先写到临时文件,验证后再 rename() 替换原文件,防止中间出错损坏原始内容:
$tempFile = tempnam(sys_get_temp_dir(), 'img_replace_');
file_put_contents($tempFile, $html);
if (file_get_contents($tempFile) !== false) {
rename($tempFile, 'page.html');
} else {
unlink($tempFile);
throw new RuntimeException('写入失败,已清理临时文件');
}
批量处理多个文件时注意内存和路径遍历边界
用 glob() 或 RecursiveDirectoryIterator 批量处理时,常见疏漏是:
-
glob('*.html')只查当前目录,子目录要用glob('**/*.html', GLOB_BRACE)(PHP 7.1+)或手动递归 - 大文件(比如 >10MB)用
file_get_contents会吃光内存,得改用流式解析(DOM 不支持流,此时只能切回正则 +fgets分块读) - 路径含中文或特殊字符,
file_get_contents可能失败,加mb_internal_encoding('UTF-8')并确认 locale 设置
小批量(几百个文件以内)、单文件不大于 2MB,直接用 DOM 方案最省心;超过就得分批 + 加内存限制判断。
DOM 解析对标签格式要求高,但胜在语义准确;正则快但容易漏边角情况。真正上线前,拿几个真实页面样本跑一遍,尤其检查带 data-src、srcset、内联 SVG 的 img 标签是否被误处理或忽略。



















