6.7 KiB
6.7 KiB
7.3 富文本处理
:::tip 概述
富文本(如文章内容)中的图片/视频也需要处理。使用 [IMG_URL] 占位符机制,支持域名变更场景。
:::
处理流程图
┌─────────────────────────────────────────────────────────────────┐
│ 写入流程(add/update) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 前端编辑器插入图片 │
│ │ │
│ ▼ │
│ 上传到临时目录 temp/日期/ │
│ │ │
│ ▼ │
│ 提交 HTML 内容到后端 │
│ │ │
│ ▼ │
│ BaseLogic::processContentFieldsOnSave() │
│ │ │
│ ├─ 解析 <img> 标签的 src │
│ ├─ 解析 <video>/<source>/<embed> 标签的 src │
│ ├─ 解析 <a> 标签的文件 href │
│ │ │
│ ▼ │
│ save_file() 迁移临时文件 → 正式目录 │
│ │ │
│ ▼ │
│ 替换为 [IMG_URL]/相对路径 占位符 │
│ │ │
│ ▼ │
│ 数据库存储含占位符的 HTML │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 读取流程(detail/pageList) │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 数据库读取含占位符的 HTML │
│ │ │
│ ▼ │
│ BaseLogic::processContentFieldsOnRead() │
│ │ │
│ ├─ 替换 [IMG_URL] 为实际域名 │
│ │ 如 https://cdn.example.com │
│ │ │
│ ▼ │
│ 返回完整 URL 的 HTML 给前端 │
│ │
└─────────────────────────────────────────────────────────────────┘
在 Logic 中配置
class ArticleLogic extends BaseLogic
{
/**
* 富文本字段
*
* @var array
*/
protected array $contentFields = ['content'];
}
配置后 BaseLogic 自动处理:
- add/update:调用
save_content()处理临时文件 - detail/pageList/allList:调用
processContentFieldsOnRead()替换占位符
为什么使用占位符?
:::tip 域名变更场景
假设初始配置 FILE.DOMAIN_URL = https://cdn.example.com,文章内容中图片 URL 为:
https://cdn.example.com/article/20260920/abc.jpg
存储时替换为:[IMG_URL]/article/20260920/abc.jpg
如果后续域名变更为 https://cdn2.example.com,只需修改 .env 中的 FILE.DOMAIN_URL,已存储的内容无需逐条更新,读取时自动替换为新域名。
:::
处理范围
save_content() 函数处理以下 HTML 标签:
| 标签 | 属性 | 说明 |
|---|---|---|
<img> |
src |
图片 |
<video> |
src |
视频 |
<source> |
src |
视频源 |
<embed> |
src |
嵌入内容 |
<a> |
href |
文件链接(仅处理带扩展名的链接) |
代码示例
// app/common.php 中的 save_content 函数
function save_content(string &$content, string $saveDir = 'article'): bool
{
$config = config('file');
$domainUrl = rtrim($config['domain_url'] ?? '', '/');
if (empty($domainUrl) || empty($content)) {
return false;
}
// 收集所有需要处理的 URL
$urlMap = [];
// 1. 匹配 <img> 标签的 src
save_content_match($content, '/<img[^>]+src=["\']?([^"\'>\s]+)/i', $domainUrl, $saveDir, $urlMap);
// 2. 匹配 <video>/<source>/<embed> 标签的 src
save_content_match($content, '/<(?:video|source|embed)[^>]+src=["\']?([^"\'>\s]+)/i', $domainUrl, $saveDir, $urlMap);
// 3. 匹配 <a> 标签的 href(指向文件的情况)
save_content_match($content, '/<a[^>]+href=["\']?([^"\'>\s]+\.\w{2,5})["\']?/i', $domainUrl, $saveDir, $urlMap);
// 批量替换
if (!empty($urlMap)) {
$content = str_replace(array_keys($urlMap), array_values($urlMap), $content);
}
return true;
}
注意事项
:::warning 富文本安全
- 富文本内容可能包含 XSS 攻击代码,建议在前端使用编辑器的安全模式
save_content只处理本站域名下的 URL,外部 URL 不做处理- 图片标签的
src必须是完整 URL(包含域名),否则无法识别 :::