laravel软删除不会自动删除关联文件,需监听trashed事件或手动调用storage::delete()清理;路径须相对disk根目录,注意权限、驱动差异及s3配置。

上传后文件没删,磁盘空间悄悄涨
Laravel 本身不会自动清理已上传但未关联的临时文件,也不会在模型软删除时顺手删掉对应文件。你调用 $model->delete(),数据库里那条记录变 deleted_at 了,但 storage/app/uploads/avatar.jpg 还稳稳躺在那里——除非你手动处理。
Storage::delete() 是最直接的删除方式
只要知道文件路径(相对 disk 根目录),就能立刻删掉。注意路径不能带 disk 的 root 前缀,比如配置中 'root' => storage_path('app'),那么传给 Storage::delete() 的必须是 'uploads/avatar.jpg',而不是 storage_path('app') . '/uploads/avatar.jpg'。
- 确认当前使用的是哪个 disk:
Storage::disk('local')或Storage::disk('s3'),不同 disk 删除逻辑一致,但底层行为不同(本地是unlink(),S3 是发 DELETE 请求) - 路径必须存在且可读写,否则会抛出
FileNotFoundException - 批量删除多个文件时,
Storage::delete(['a.jpg', 'b.png'])比循环调用更高效 - 对 S3 等远程 disk,
delete()不会触发事件或回调,别指望它自动同步 CDN 或更新数据库字段
软删除模型时,文件得自己配钩子删
模型启用软删除后,delete() 不触发 deleting 事件,只触发 trashed。想在标记为删除时顺手删文件,得监听 trashed 事件,而不是 deleting。
- 在模型中写:
protected $dispatchesEvents = ['trashed' => UserTrashed::class];,然后在监听器里调用Storage::delete($user->avatar_path) - 如果用的是
laravel-uploadable这类扩展包,它通常会在trashed时自动清理,但前提是配置了deleteOnTrashed = true - 别在
restoring事件里“恢复文件”——文件一旦删了就没了,除非你事先备份过原始二进制或存了多个版本
清理失败常卡在权限、路径和驱动差异上
报 Permission denied 或静默失败?大概率不是代码问题,而是环境配置偏差。
- 本地开发用
localdisk 时,确保storage/app(或你自定义的public/uploads)目录权限为755或775,且 Web 服务器用户(如www-data)有写权限 - S3 disk 删除失败,先检查
AWS_SECRET_ACCESS_KEY是否被 .env 缓存污染(运行php artisan config:clear再试) - 用
Storage::exists()先判断文件是否存在,再删,避免无意义异常;但注意:S3 上exists()是 HTTP HEAD 请求,有网络开销 - 别依赖
request()->file('xxx')->path()的返回值做删除——那是临时文件路径,上传完就该被 Laravel 自动清掉,你再去删它会报错
真正容易被忽略的点是:软删除不等于物理删除,而文件系统没有“软删除”概念。你得自己决定——这条记录只是暂时隐藏,还是彻底废弃?前者要留文件,后者必须删,且最好在事务外异步做,避免阻塞主流程。











