织梦取文章第一张图:3种方案+47类内容兜底格式实战
织梦 DedeCMS 列表页缩略图缺失怎么办?保哥用三套方案补齐:旧版 substr+jpg 替换法(局限大)、基于 -lp 标识的 str_replace 兼容多格式(推荐)、litpic 缺失时回退正文 preg_match 抓图(最完整);附性能优化、批量回写、与 WP/Typecho 对比。
本文目录
保哥从 2010 年前后开始接触织梦 DedeCMS,那个时候做企业站、行业门户、地方资讯站,DedeCMS 几乎是标配。十几年下来,用织梦搭过的站点不下两百个,光是"调用文章第一张图"这一个看似不起眼的需求,就在不同站点上反复折腾过几十次。这篇文章把保哥踩过的坑、改过的代码、最终落地的方案完整记录下来,希望能帮到还在用织梦的朋友。
为什么需要单独写一个"取第一张图"的函数
织梦 DedeCMS 的列表模板里,调用缩略图通常用 [field:litpic/] 或 {dede:field name='litpic'/}。但是实际项目里,很多场景下编辑根本不会主动上传缩略图,他们习惯把图片直接贴在正文里就完事。这就导致列表页大量缺图,封面只能显示一个默认占位图,整体观感非常差。
保哥早期的解决思路有三种:
- 思路一:让编辑必须上传缩略图,否则不允许发布。但这条规则在团队大、外包多的场景里几乎执行不下去。
- 思路二:用计划任务定时扫描数据库,把正文里第一张图自动写入 litpic 字段。这种做法对服务器有压力,而且修改图片后不会同步更新。
- 思路三:在模板渲染层做一个 fallback——如果 litpic 为空就从 body 里正则取第一张图。这是最稳妥的方案,本文重点讲这种方式的几个变体。
搞清楚需求后会发现,织梦自带的缩略图机制其实有一个隐藏规则:上传原图后,系统会在文件名末尾追加 -lp 这样的标识符(不同版本略有差异,老版本是直接补 7 位字符再加扩展名),缩略图和原图存放在同一目录。理解这个规则是后面所有方法的基础。
流传最广的旧方法:删除末尾 7 位字符
网上随便一搜"织梦调用第一张图",跳出来的几乎都是下面这段代码。保哥最早也是从某个论坛抄来的,用了大概两年才发现它的问题。
打开 /include/common.func.php,在文件末尾追加:
<?php
// 取第一张图地址(旧方法,仅适用于 jpg)
function firstimg($str_pic){
// 删除缩略图字符串最后七位,再补上 .jpg 扩展名
$str_sub = substr($str_pic, 0, -7) . ".jpg";
return $str_sub;
}然后在列表模板里这样使用:
<!-- 在 list 标签内嵌套调用 -->
[field:litpic function="firstimg('@me')"/]
<!-- 单独调用 -->
{dede:field name='litpic' function="firstimg(@me)"/}这段代码为什么不靠谱?保哥实际跑过几个站后总结出三个致命问题:
- 写死 .jpg 扩展名:如果原图是 png、gif、webp,调用出来全部是 404。
- 依赖文件名长度:织梦不同版本生成的缩略图后缀不一样,盲目截掉 7 位会破坏文件名。
- 没有空值保护:如果 litpic 字段为空(编辑根本没上传图),函数会返回
.jpg这个无效字符串。
如果你的站点 100% 用 jpg,并且确认织梦版本生成的缩略图后缀就是 7 位字符,这段代码确实能工作。但放在 2026 年的环境下,几乎没有任何站点能满足这两个前提——WebP/AVIF 格式逐渐成主流,老方法注定淘汰。
改良方法:基于 -lp 标识符的字符串替换
这是保哥目前推荐的写法,兼容多种图片格式,逻辑也更清晰。同样是在 /include/common.func.php 里追加函数:
<?php
// 取第一张图地址(兼容 jpg/png/gif/webp)
function firstimg($str_pic){
// 把缩略图标识 -lp 替换掉,得到原图地址
$str_sub = str_replace("-lp", "", $str_pic);
return $str_sub;
}模板里的调用方式没有变化:
[field:litpic function="firstimg('@me')"/]这种写法的好处是不关心扩展名——不管原图是 jpg、png、gif、webp 还是 avif,函数只把 -lp 标识符抹掉,扩展名保持原样。前提是你的织梦版本在生成缩略图时确实使用了 -lp 这个后缀。如果是更老的版本,可能用的是 .thumb.jpg、.litpic.jpg 这类规则,需要根据实际情况调整 str_replace 的参数。
怎么确认你站点上的缩略图规则?保哥的做法是随便打开一篇带图的文章,在数据库 dede_archives 表里查 litpic 字段,对比一下原图路径和缩略图路径,前后差异就是要替换的字符串。一分钟就能搞清楚。
保哥的最终版本:从正文里直接抓第一张图
前两种方法都依赖 litpic 字段非空。如果编辑压根没设置缩略图,怎么办?这时就需要从 body 字段里正则提取第一张 <img> 标签。
保哥在客户站点上线的最终版函数是这样的:
<?php
// 综合方案:优先用 litpic,缺失时从正文抓第一张图
function firstimg($str_pic, $arcid = 0){
// 第一步:litpic 非空且文件存在,按 -lp 规则替换
if (!empty($str_pic) && strpos($str_pic, '-lp') !== false) {
return str_replace("-lp", "", $str_pic);
}
// 第二步:litpic 为空,从 addonarticle 表里抓 body
if ($arcid > 0) {
global $dsql;
$row = $dsql->GetOne("SELECT body FROM `#@__addonarticle` WHERE aid=$arcid");
if ($row && !empty($row['body'])) {
preg_match('/<img[^>]+src=["\']?([^"\'>\s]+)/i', $row['body'], $match);
if (!empty($match[1])) {
return $match[1];
}
}
}
// 第三步:兜底返回默认占位图
return '/static/images/default-cover.jpg';
}模板里调用时把 aid 传进去:
[field:litpic function="firstimg('@me', ~aid~)"/]这样三层逻辑就齐了:缩略图存在时直接还原原图,缺失时回正文抓,正文也没图就走兜底。保哥在好几个内容站上跑了三年多,这套逻辑一直没出过问题。
性能担心吗?说实话,列表页每篇文章多查一次 addonarticle 表确实有开销。如果列表页一次出 20 条,最坏情况就是 20 次额外查询。在 5 万条文章规模以下、并且加了 OPCache 的环境里,多出来的延迟基本感知不到。如果数据量更大,建议把这个 fallback 写成一次性脚本,把抓出来的第一张图回写到 litpic 字段,从根上避免运行时查询。
织梦标签的两种调用形式区别
用织梦多年,新手最常问的就是:什么时候用 [field:.../],什么时候用 {dede:field name='...'/}?保哥这里一次说清楚。
[field:xxx/]形式:只能写在 list 类标签内部,例如{dede:list}...{/dede:list}、{dede:arclist}...{/dede:arclist}之间。它是循环上下文里的字段占位符。{dede:field name='xxx'/}形式:用在文章详情页、单页模板的根作用域里,直接渲染当前文档的字段值。
上面写的 firstimg 函数两种调用方式都演示了,记得 list 内部用方括号、详情页用花括号,混着写会直接渲染失败。
另外说一个细节:@me 在两种语法里都表示"当前字段的值"。所以 function="firstimg('@me')" 实际就是把 litpic 当前值传进 firstimg 函数,等价于 PHP 里写 firstimg($litpic)。
常见踩坑场景与排查思路
保哥这些年帮人排错,发现"调用第一张图失败"的问题集中在以下几个点。如果你照着上面写完不出图,按这个清单查一遍基本都能定位。
- 函数没生效:确认
common.func.php里函数加在?>之前,并且文件保存成 UTF-8 无 BOM。BOM 会让函数注册失败。 - 静态化没更新:织梦默认生成静态 HTML,模板改了之后必须在后台"更新主页 HTML""更新栏目 HTML""更新文档 HTML"一遍,否则前台还是旧内容。
- 图片路径带域名:如果 litpic 存的是绝对 URL(带
http://),str_replace不影响域名部分,但要小心和图片防盗链规则冲突。 - 图片附件已删除:缩略图存在但原图被清理过,会导致还原后的路径 404。可以在函数里加
file_exists判断,找不到就走兜底图。 - 正则贪婪问题:从 body 抓 img 时如果
src用了双引号嵌套,正则要小心写。保哥推荐的写法已经处理了单双引号和无引号三种情况。 - OPCache 残留:修改完
common.func.php后如果开启了 OPCache,旧字节码可能还在缓存里。service php-fpm reload一下保险。 - 编辑器自动加 alt 属性:百度编辑器某些版本会在 src 之前插入 alt 属性,正则的
src=锚点无法匹配。需要把正则的 src 前置位放宽。
性能优化建议
当文章数突破 10 万 + 之后,firstimg 函数里查 addonarticle 表的 fallback 会成为列表页瓶颈。保哥推荐三种优化路径:
路径一:批量回写 litpic 字段。写一个一次性脚本,把所有 litpic 为空的文章扫一遍,从 body 抓第一张图回写到 litpic。脚本伪代码:
$rows = $dsql->GetAll("SELECT a.aid, b.body FROM dede_archives a
LEFT JOIN dede_addonarticle b ON a.id=b.aid
WHERE a.litpic='' OR a.litpic IS NULL");
foreach ($rows as $r) {
if (preg_match('/<img[^>]+src=["\']?([^"\'>\s]+)/i', $r['body'], $m)) {
$dsql->ExecuteNoneQuery("UPDATE dede_archives SET litpic='" . $m[1] . "' WHERE id=" . $r['aid']);
}
}跑一次后所有历史文章都补上了 litpic,模板里只用方法二即可,不再需要 fallback 查询。
路径二:发布钩子。在 article_add.php、article_edit.php 的入库函数后面加一段,发布时如果 litpic 为空就自动从 body 抓。这样新文章不会再产生空 litpic,只需要处理一次历史数据。
路径三:Memcached/Redis 缓存。把 firstimg 的结果按 aid 缓存 1 小时,前 1 小时之内的相同 aid 直接返回缓存,避免重复查询。这种适合大流量站点,配合 OPCache 几乎零延迟。
与其他 CMS 的对照参考
"取文章第一张图"这个需求几乎所有 CMS 都遇到过,对照参考能加深理解:
- WordPress:
get_the_post_thumbnail_url()取 featured image,缺失时用preg_match('/<img.*?src="(.*?)"/', $post->post_content)回退到正文。 - Typecho:通过
$archive->content过滤插件实现,或者写一个 helper 函数get_first_image($content)。 - Joomla:插件
plg_content_jcomments之类的扩展自带"自动取首图"功能。 - 帝国 CMS:通过自定义字段 + 系统模型钩子,发布时自动抓正文首图填充缩略图字段。
- Discuz:门户文章的 picflag 字段判断有无配图,配合
parsehtml函数从主题正文抓图。
所有 CMS 的实现思路殊途同归——"先用专属缩略图字段,回退到正文图,最后兜底默认图",本文的三层逻辑可以直接迁到任何 CMS 上。
九-A、SEO 视角下的首图策略
"取第一张图"看似只是一个视觉补丁,从 SEO 角度其实牵连出更多需要思考的问题。保哥过去几年帮客户做织梦站 SEO 时积累了一些经验:
列表页首图与 Open Graph。社交平台(微博、微信公众号、Twitter、Facebook)抓取链接预览时读 <meta property="og:image">。如果你的详情页 og:image 用的是 firstimg 函数取出的图,列表页和社媒预览的封面就能一致,提升品牌识别度。具体写法:在 article_article.htm 模板的 head 区域加 <meta property="og:image" content="{dede:field name='litpic' function='firstimg(@me)'/}">。
结构化数据 Article 类型的 image 字段。Google 的 Article schema 推荐 image 字段填一张高质量主图(建议 1200×630 或更大)。如果 firstimg 取出的图分辨率不够,搜索结果摘要的图位会被 Google 自动忽略。建议在站点上传规范里强制要求文章首图最低 1200×630,并通过模板编辑器侧加一道前端校验。
图片 SEO 的 alt 文本。从 body 抓出来的 img 标签往往没有 alt 属性,但作为列表页缩略图渲染时也不需要 alt。真正影响 SEO 的是详情页正文里的 alt,这个跟 firstimg 函数没关系,需要在编辑发文时养成填写 alt 的习惯,或者在 article_add.php 入库时自动生成 alt(用文章标题作为 alt 文本是常见兜底)。
WebP/AVIF 支持的 SEO 影响。Google 在 2026 年对页面性能的权重比以往更高,启用 WebP 通常能让 LCP 下降 30%-40%。建议站点全面切换到 WebP 输出,可以在 nginx 层用 map 指令做 Accept 头协商,自动给支持 WebP 的浏览器返回 .webp 文件,老浏览器返回 .jpg。这种透明协商方案不需要改 firstimg 函数。
调试技巧:怎么定位"为什么没图"
列表页有些卡片显示了图,有些卡片显示不了图,这是织梦 firstimg 部署后最常见的"中段问题"。保哥的排查流程是这样的:
- 用 Chrome DevTools 看 Network 面板。404 的图片会以红色标记,点开看请求 URL,对比下数据库里实际存的 litpic 路径,找出函数处理后的 URL 错在哪。
- 临时关闭 firstimg 包装。把
function="firstimg('@me')"改成不带 function,让模板直接输出原始 litpic 字段。如果不带 function 时图正常显示但带上后 404,说明问题出在 firstimg 函数本身。 - 逐条 echo 调试。在 firstimg 函数里加
echo "[$str_pic]<br>";,把进函数前后的值都打出来,肉眼对比哪条记录的输入异常。 - 查 dede_archives.litpic 字段。
SELECT id, title, litpic FROM dede_archives WHERE litpic IS NOT NULL AND litpic != '' ORDER BY id DESC LIMIT 50;看看最近 50 条文章的 litpic 实际存了什么——很多时候问题是数据脏,比如某条记录把 alt 文本误存到了 litpic 字段。 - 对比缩略图与原图实际文件。SSH 到 uploads 目录里,
ls -la 202401/看是否真的存在原图和缩略图两个版本。如果原图被早期清理任务删掉了,函数还原出的路径自然 404。
调试踩过几次后会发现,"为什么没图"通常 80% 是数据问题、15% 是函数逻辑、5% 是模板调用方式。先排数据再排代码,效率最高。
如果你管的是大型站点(>1 万篇文章),强烈建议把这套排查流程做成一个后台脚本:每天定时扫描 dede_archives 里 litpic 异常的记录(空值、不存在的文件、404 链接),生成一份运维报告邮件给编辑组。这样问题暴露得早,反应也快,不会等到读者反馈"封面图缺失"才发现。保哥过去帮一家本地媒体站搭这套自动巡检后,"列表页缺图"工单数量从每月十几条降到几乎为零。这种边角细节做扎实了,整个站点的视觉品质会有肉眼可见的提升。
另一个值得一提的运维实践是缩略图统一规格。织梦默认缩略图比例与编辑上传的原图比例一致,但列表卡片是固定比例(比如 16:9),不一致会导致卡片大小参差。建议在 article_add.php 入库时对 litpic 做一次 ImageMagick 中心裁剪,统一裁成 16:9 或 4:3,这样列表页视觉规整度直线上升,且不影响详情页的原图展示。这一步可以在 firstimg 函数之外独立完成,是属于上传流程的优化范畴,但跟首图策略相辅相成。
最后一点是性能监控数据要定期复盘。每月把 list 页的平均 PHP 处理时间、SQL 执行次数对比上月,如果发现回退到正文抓图的比例上升、SQL 数明显增加,说明 litpic 字段的覆盖率在下降,需要回头看是不是发文流程出了问题,或者老文章批量回写脚本要再跑一次。把指标握在手里,问题在变成事故前就能被识别——这才是十几年织梦运维沉淀下来的真正经验。
常见问题解答
Q1:站点有上千篇文章已经发布,能不能一次性把第一张图回写到 litpic 字段?
可以,写一个迁移脚本即可。思路是遍历 dede_archives 表里 litpic 为空的记录,到 dede_addonarticle 表对应行抓 body,正则取出第一张 img 的 src,更新回 litpic。在凌晨流量低的时候跑一次,几千条文章十几分钟就完事,跑完之后模板里就不需要 fallback 函数了。
Q2:织梦升级到最新版本后,缩略图标识符还是 -lp 吗?
不一定。DedeCMS 5.7 SP2、5.8、还有各种二开版本(WeCenter、织梦增强版)的标识符都不完全相同。保哥的建议是:每次升级或换版本部署后,都手动验证一下数据库里 litpic 字段的命名规则,再调整 str_replace 的参数。不要假设规则不变。
Q3:用上面的方法调出来的图片在手机端显示变形怎么办?
这个不是函数本身的问题,是织梦自动给 img 标签加了 width/height 内联样式,固定了尺寸。需要修改 arc.archives.class.php,用正则把 width/height 属性剥掉。这个话题保哥另外写过一篇专门的文章,可以搜"织梦去掉图片长宽属性"找到。
Q4:除了正则,还有更优雅的方式从 body 里抓图吗?
如果服务器 PHP 版本 7.0+,推荐用 DOMDocument 解析。代码稍长一点,但比正则更稳健,能正确处理嵌套属性、自闭合标签和 HTML 实体编码。性能上 DOMDocument 对小段 HTML 解析非常快,列表页用完全没问题。
Q5:如果文章里第一张图是水印图、二维码这种小图怎么办?
可以在 firstimg 里加一道筛选逻辑——抓出所有 img,按 src 路径过滤掉包含 watermark、qrcode、icon 等关键字的小图,取第一张"真实内容图"。或者按图片尺寸判断(需要在 server-side 用 getimagesize 读取,性能开销较大)。多数情况下"取第一张"就够用,水印图问题占比 < 5%。
Q6:函数能不能放在主题模板里而不是 common.func.php?
不能直接放在 .htm 模板里——织梦模板是预编译的,不支持任意 PHP。但可以放在 /include/extend.func.php 这个扩展函数文件里,效果跟 common.func.php 一样。区别在于 extend.func.php 不会被升级覆盖,更适合自定义函数集中存放。
Q7:能不能让 firstimg 兼容 WebP/AVIF 等新格式?
本文方法二(基于 -lp 替换)天然兼容所有图片格式,因为它不关心扩展名。如果你的站点用 CDN 自动转 WebP,只需要确认原图存的是 jpg/png 路径即可,CDN 会在响应层自动协商格式。
写在最后
织梦 DedeCMS 在 2026 年看确实是"上个时代"的产品,但保哥手里还有不少老站跑在它上面,迁移成本太高没法立刻替换。这种工具型函数虽然简单,但日积月累攒下来就是经验沉淀。把每一个小坑都记录清楚,下次再遇到同类问题就不用从零摸索。
如果你的项目正在用织梦,希望这篇能帮你省下几个小时调试时间。有更好的写法或者新版本的兼容问题,欢迎在评论区告诉保哥,会持续更新这篇文章。下一篇打算写"织梦发布钩子全集",把入库时的自动化补全(litpic 字段、关键词、自动摘要)一次性讲清楚。
FAQPage + Article AI 引用友好版
织梦 DedeCMS 列表页缩略图缺失怎么办?保哥用三套方案补齐:旧版 substr+jpg 替换法(局限大)、基于 -lp 标识的 str_replace 兼容多格式(推荐)、litpic 缺失时回退正文 preg_match 抓图(最完整);附性能优化、批量回写、与 WP/Typecho 对比。
- 织梦图片
- 织梦调用
- DedeCMS函数
- litpic字段
- 织梦模板
- 织梦CMS教程
title: 织梦取文章第一张图:3种方案+47类内容兜底格式实战 author: 张文保 (Paul Zhang) — PatPat SEO 经理 url: https://zhangwenbao.com/dedecms-method-first-picture-of-the-article.html published: 2020-11-20 modified: 2026-05-16 source-type: First-hand expert commentary language: zh-CN license: CC BY-NC-SA 4.0 (要求保留原文链接与作者归属)
本文标题:《织梦取文章第一张图:3种方案+47类内容兜底格式实战》
本文链接:https://zhangwenbao.com/dedecms-method-first-picture-of-the-article.html
版权声明:本文原创,转载请注明出处和链接。许可协议: CC BY-NC-SA 4.0