
本文介绍在 Laravel 中使用 Summernote 富文本编辑器时,如何确保删除文章(Post)的同时,一并清除其正文中嵌入的 Base64 图片及主图(通过 addMedia() 上传的封面图),避免服务器残留垃圾文件。
本文介绍在 laravel 中使用 summernote 富文本编辑器时,如何确保删除文章(post)的同时,一并清除其正文中嵌入的 base64 图片及主图(通过 addmedia() 上传的封面图),避免服务器残留垃圾文件。
在使用 Summernote 编辑内容时,用户常通过粘贴或拖拽上传图片,这些图片默认以 Base64 编码形式内联在 HTML 的 <img src="data:image/..."> 中;你的 store 方法已正确将其解码并保存为独立 PNG 文件(路径如 /summernoteUploads/17123456780.png),同时将正文中的 src 替换为相对路径。此外,你还通过 addMedia($mainImage)->toMediaCollection() 保存了封面图(依赖 Laravel Media Library)。但当前 destroy 方法仅调用 $post->delete(),未触发任何资源清理逻辑,导致图片文件长期滞留——这不仅浪费磁盘空间,还可能引发安全与维护风险。
要实现「删除文章即清理所有关联图片」,推荐采用 Eloquent 模型事件(Laravel 官方推荐方式),而非在控制器中硬编码清理逻辑,以保持职责分离与可维护性。你已在答案中提到 booted() + static::deleted() 的方案,下面给出完整、健壮的实现:
✅ 步骤一:在 Post 模型中注册删除后钩子
在 app/Models/Post.php 中添加以下静态启动方法(Laravel 8+ 推荐写法):
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected static function booted()
{
// 处理软删除(若启用 softDeletes)
static::deleted(function ($post) {
$post->cleanupAssociatedImages();
});
// 处理强制删除(如使用 forceDelete() 或未启用软删除)
static::forceDeleted(function ($post) {
$post->cleanupAssociatedImages();
});
}
private function cleanupAssociatedImages()
{
// 1. 清理通过 Spatie Media Library 上传的主图(如封面图)
$this->clearMediaCollection(); // 删除所有媒体文件(含缩略图等)
// 2. 清理 Summernote 正文中保存的 Base64 图片
$dom = new \DomDocument();
libxml_use_internal_errors(true); // 忽略 HTML 解析警告(如缺少 doctype)
$dom->loadHtml($this->content, LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD);
$imageTags = $dom->getElementsByTagName('img');
foreach ($imageTags as $img) {
$src = $img->getAttribute('src');
// 匹配 /summernoteUploads/xxx.png 类型路径(需与 store 中生成规则一致)
if (str_starts_with($src, '/summernoteUploads/') && pathinfo($src, PATHINFO_EXTENSION) === 'png') {
$fullPath = public_path($src);
if (file_exists($fullPath)) {
unlink($fullPath);
}
}
}
}
}
? 注意事项:
- 若你启用了
SoftDeletes,请确保deleted事件在forceDeleted之外也覆盖软删除场景;上述代码已兼顾两者。libxml_use_internal_errors(true)可防止因 HTML 内容不规范(如自闭合标签缺失)导致解析失败。- 路径匹配建议使用
str_starts_with()+ 扩展名校验,避免误删其他资源(如/images/logo.png)。- 若你使用的是 Laravel 9.x 或更高版本,
static::deleted()和static::forceDeleted()是首选;旧版本可用static::creating()等替代,但语义不如新版清晰。
✅ 步骤二:更新控制器 destroy 方法(可选优化)
虽然模型事件已接管清理,但控制器仍建议做事务封装与错误提示增强:
public function destroy(Post $post)
{
try {
$post->delete(); // 触发 deleted / forceDeleted 事件
return redirect()->back()->with('status', 'Post and all associated images deleted successfully.');
} catch (\Exception $e) {
\Log::error('Failed to delete post ' . $post->id . ': ' . $e->getMessage());
return back()->with('error', 'Failed to delete post. Please try again.');
}
}
✅ 额外建议:增强健壮性
-
日志记录:在
cleanupAssociatedImages()中添加\Log::info("Cleaned {$count} Summernote images for post {$this->id}");便于审计。 -
异步清理(高并发场景):若图片量极大,可将
unlink()放入队列任务,避免请求超时。 -
CDN/对象存储适配:若图片存于 S3 或 Cloudinary,请替换
unlink(public_path(...))为对应 SDK 删除调用(如Storage::disk('s3')->delete($path))。
通过以上设计,你实现了真正意义上的「原子化删除」:一次 delete() 调用,自动清理数据库记录、媒体库文件及富文本内联图片,系统更健壮、运维更省心。











