ThinkPHP 不会自动删除上传的临时文件,因其设计依赖 PHP 脚本结束时清理,而实际中常因异常、手动绕过流程或移动失败导致残留;应通过定时任务清理 runtime/temp 目录或在 moveTo() 成功后主动 unlink($file->getTmpName())。

为什么上传的临时文件没被自动删掉
ThinkPHP 默认不会自动清理上传后的临时文件,哪怕你用了 moveTo() 方法把文件移走了,原始临时文件仍留在系统临时目录(如 /tmp 或 runtime/temp)里。这不是框架 bug,而是设计使然:PHP 本身只在脚本结束时自动清理 $_FILES 对应的临时文件,但 ThinkPHP 的上传类(think\File)在调用 moveTo() 后会主动 unlink() 原临时文件——前提是没出错、没中断、没手动绕过上传流程。
常见踩坑点:
- 直接用
$_FILES['xxx']['tmp_name']手动处理,没走think\File流程,PHP 不会帮你删 - 上传成功后抛异常或 die(),导致
moveTo()后的清理逻辑没执行完 - 用
upload()->rule('uniqid')等规则生成新名,但移动失败(比如目录不可写),临时文件就卡在那里了
用命令行定时任务清理 runtime/temp 下的旧文件
最稳妥的方式是不依赖 PHP 运行时清理,改用系统级定时任务定期扫除 runtime/temp 目录下超过指定时间的文件。ThinkPHP 的临时上传文件默认都存在这里(除非你显式配置了 upload.tempDir)。
实操建议:
立即学习“PHP免费学习笔记(深入)”;
- Linux 下用
cron,例如每天凌晨两点清理 24 小时前的文件:0 2 * * * find /path/to/your/thinkphp/runtime/temp -type f -mmin +1440 -delete
- Windows 可用任务计划程序 +
forfiles命令,例如清理 1 天前的:forfiles /p "D:\project\runtime\temp" /d -1 /c "cmd /c del @path"
- 注意路径权限:确保执行用户对
runtime/temp有读写权限,否则find或forfiles会跳过或报错 - 别误删
runtime/cache或runtime/log—— 它们结构不同,但都在同级目录,加-type f和精确路径能避免误伤
在上传逻辑里加手动 unlink 防止漏删
如果你必须在上传过程中确保临时文件立刻消失(比如上传大文件时内存/磁盘吃紧),不能等定时任务,就得在代码里补一层保险。
关键点:
- 只在确认
moveTo()成功后才unlink(),否则可能删掉还没移走的文件 - 不要直接
unlink($_FILES['file']['tmp_name']),因为 ThinkPHP 可能已重命名或复制过临时文件 - 正确做法是拿到
think\File实例后,用其getTmpName()方法获取真实临时路径:$file = $this->request->file('file');<br>if ($file && $file->isValid()) {<br> $saved = $file->move($savePath);<br> if ($saved) {<br> @unlink($file->getTmpName()); // 主动删临时文件<br> }<br>} -
@unlink()加@是防止文件已被删或权限不足时报 warning,不影响后续逻辑
自定义上传驱动时注意临时文件生命周期
如果你用了云存储(如阿里云 OSS、腾讯 COS)并实现了自定义上传驱动,think\File 的 getTmpName() 可能返回空或无效路径,因为文件根本没落地到本地临时目录。
这时清理逻辑要换思路:
- 本地中转上传(先
moveTo()到本地再传云):按前面方法删本地临时文件即可 - 直传云(前端直传,后端只做签名或回调校验):根本没临时文件,无需清理
- 驱动内流式上传(
fopen($file->getTmpName(), 'r')后直接curl推送):记得fclose()后立即unlink(),别等脚本结束 - TP6.1+ 的
UploadFile类支持setTempName(),可用来统一管理临时路径,方便集中清理
真正容易被忽略的是:不同部署环境(容器、宝塔、共享主机)对 /tmp 的清理策略不同,有些会定时清空整个系统临时目录,反而把你存的 session 或缓存也干掉了。所以永远优先清理自己应用的 runtime/temp,而不是依赖系统行为。



















