当前位置:首页 > 文章列表 > 文章 > php教程 > PHP-FPM 上传临时文件越积越多:upload_tmp_dir、清理时机与失败回收

PHP-FPM 上传临时文件越积越多:upload_tmp_dir、清理时机与失败回收

来源:17golang原创 2026-08-09 07:16:55 0浏览 收藏

凌晨收到磁盘告警,通常不会第一时间想到根因出在上传临时文件上。如果你的PHP-FPM服务器上的 /tmp 或者自定义 upload_tmp_dir 占用空间持续上涨,同时最近上传接口刚出现过超时、进程重启或是业务报错,临时文件没落到最终存储路径也没被及时回收,基本就是头号排查目标。处理这类问题的核心不是登服务器删掉存量文件就完事,而是把临时文件从创建、校验、移动到失败清理的整条链路逐段核对清楚。

实践要点
  • upload_tmp_dir 只决定PHP接收上传数据的临时存放位置,和业务最终的文件目录完全是两回事。
  • 只有通过 is_uploaded_file() 校验并成功 move_uploaded_file() 后,临时文件才算完成归属交接。
  • 异常分支要在同一次请求流程里清理临时文件,定时任务只做兜底,不能替代代码层面的失败回收逻辑。
  • 清理之前先按文件生成时间、PHP-FPM活跃请求列表和目录白名单做校验,避免误删还在传输中的合法文件。

先确认增长来源:是临时目录满了,还是最终业务目录在变大

我更推荐先摸清楚文件增长的位置和时间分布,再动手输清理命令。下面给出的操作只会读取目录信息,不会改动或删除任何文件:

php --ini | grep -E 'Loaded Configuration|Scan for additional'
php -r 'echo ini_get("upload_tmp_dir"), PHP_EOL;'
find /tmp -maxdepth 1 -type f -mmin +30 -printf '%TY-%Tm-%Td %TH:%TM %s %p\n' | sort -k3 -nr | head -20
df -h /tmp /var/www/app/storage/uploads

如果上传临时目录里的文件是批量生成、生命周期短、大小和请求体特征匹配的状态,而业务最终存储目录的体积没有同步上涨,问题大概率出在数据接收完成后没走完交接流程。要是新生成的文件都集中在最近几分钟,先别按统一时间阈值批量清理,很可能还有耗时较长的慢上传请求正在运行。

PHP上传文件实际经过哪些状态

PHP收到上传请求后,会先把请求体里的文件数据写入临时位置,再把对应的元数据放到 $_FILES 里。业务代码得把这个临时文件当成还没完成交接的中转资源,而不是已经属于业务最终目录的正式文件。下面这张图对应的就是从接收、校验到移动或者失败回收的完整最小链路。

PHP-FPM 文件上传临时文件从接收、is_uploaded_file 校验到 move_uploaded_file 或失败回收的数据生命周期

优先检查error属性和is_uploaded_file判断

不要只判断 $_FILES['attachment']['tmp_name'] 文件是否存在。上传大小超限、请求中途断开、临时目录不可写这类场景下,error 才是更早的分流判断信号:

$file = $_FILES['attachment'] ?? null;
if (!is_array($file) || ($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
    http_response_code(400);
    throw new RuntimeException('upload rejected');
}

$tmp = $file['tmp_name'];
if (!is_uploaded_file($tmp)) {
    http_response_code(400);
    throw new RuntimeException('temporary upload is invalid');
}

这时候先别急着改 upload_max_filesize 配置。如果错误码是 UPLOAD_ERR_PARTIAL,优先排查客户端断连、反向代理超时和PHP-FPM单请求最大时长限制;如果是 UPLOAD_ERR_CANT_WRITE,再去核对临时目录权限和磁盘inode占用情况。

落地处理:把成功交接和失败回收写在同一条逻辑路径里

最终存储的文件名不要直接沿用用户上传的原始名称。先生成业务侧唯一ID,再把文件移动到按日期分层的业务目录里;所有校验失败的分支,都尽量在当前请求结束前把临时文件清理掉。

$targetDir = __DIR__ . '/storage/uploads/' . date('Y/m/d');
if (!is_dir($targetDir) && !mkdir($targetDir, 0750, true)) {
    throw new RuntimeException('target directory unavailable');
}

$extension = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
$allowed = ['jpg', 'jpeg', 'png', 'pdf'];
if (!in_array($extension, $allowed, true)) {
    throw new RuntimeException('file type rejected');
}

$target = $targetDir . '/' . bin2hex(random_bytes(16)) . '.' . $extension;
if (!move_uploaded_file($tmp, $target)) {
    throw new RuntimeException('temporary file handoff failed');
}

// 成功移动后,$tmp 不再是待回收对象
unset($tmp);

生产环境代码里,还要提前定好数据库记录和文件交接的先后顺序:如果先写数据库再移动文件,移动失败时必须给这条记录加“待补偿”状态;如果先移动文件再写数据库,数据库写入失败时就得把刚生成的目标文件丢进补偿队列。两种顺序都能正常跑通,只有逻辑含糊不清才会产生没人认领的孤儿文件。

给所有异常分支加窄范围的临时文件清理动作

$tmp = null;
try {
    $file = $_FILES['attachment'] ?? null;
    // 省略字段、大小和内容类型校验
    $tmp = $file['tmp_name'];
    if (!is_uploaded_file($tmp)) {
        throw new RuntimeException('invalid upload');
    }
    // 生成目标路径并完成移动
    $ok = move_uploaded_file($tmp, $target);
    if (!$ok) {
        throw new RuntimeException('handoff failed');
    }
    $tmp = null;
} finally {
    if (is_string($tmp) && is_file($tmp)) {
        unlink($tmp);
    }
}

finally 不是绝对的保险机制。PHP-FPM被强制终止、宿主机意外断电这类场景下,当前请求根本没有机会运行后续的清理代码,所以还是需要一个只处理“远超正常生命周期的老文件”的兜底定时任务。

紧急回滚处理:先暂停入口,再清理确认过期的文件

收到磁盘告警时,先把上传入口切到维护状态或者临时限制大文件传输,不要一边高并发写入一边清理同一个目录。确认没有长时间运行的慢上传请求后,只处理超过预设保留窗口、且不在活跃进程占用范围内的普通文件:

# 先观察,不删除
find /srv/php-upload-tmp -maxdepth 1 -type f -mmin +60 -printf '%T@ %s %p\n' | sort -n | tail -20

# 维护窗口内按白名单清理,保留最近 60 分钟文件
find /srv/php-upload-tmp -maxdepth 1 -type f -mmin +60 -delete

如果执行清理后磁盘空间没有明显回升,去检查inode占用、容器挂载点映射关系和PHP-FPM实际加载的配置文件。php --ini 读取到的CLI模式配置不一定等于FPM池加载的生效配置,必要时可以在临时健康检查页面输出 ini_get('upload_tmp_dir') 做确认,排查完就立刻删掉这段临时代码。

告警验证:怎么确认不是删完临时文件就临时凑合用

空间恢复后至少完整观察一个上传业务高峰时段,重点关注三个指标:临时目录总文件数、临时目录总占用字节、上传请求失败率。单次 df -h 告警变绿只能说明磁盘空间暂时释放了,不能证明异常流程的失败回收逻辑已经闭环。

检查项通过信号异常时优先排查
临时文件年龄分布业务高峰过后老文件数量持续下降失败分支逻辑、FPM重启策略、目录权限配置
上传请求错误码UPLOAD_ERR_OK 占绝大多数反向代理限制、链路超时、磁盘写入状态
最终文件与数据库记录文件和数据库状态可以互相精准定位文件移动与入库逻辑的补偿状态配置

后续优化:把临时文件状态变成可观测的业务信号

日志里记录上传请求ID、错误码、临时文件目录、文件字节数和交接耗时,但不要记录完整的客户端原始文件名或者文件内容。目录监控可以按文件数和总字节数分别设置阈值,比如文件数连续10分钟上涨但业务最终目录体积没有同步增加,就触发低级告警;磁盘使用率超过85%时直接触发限制大文件上传的预案。

还要把清理任务做成全配置可管控的模式:文件保留窗口、允许扫描的目标目录、单次最大删除数量都从配置中心读取,新配置首次上线时只输出待清理候选列表不实际执行删除。这样哪怕线上配置写错,也能先停下任务调整,不至于面对已经执行完的批量删除结果。

PHP-FPM 上传临时目录磁盘增长、错误码和安全清理窗口的工程证据场景

常见问题

upload_tmp_dir 和 sys_temp_dir 是同一个配置吗?

不是。upload_tmp_dir 专门控制文件上传产生的临时文件存放位置;sys_temp_dir 是PHP通用系统临时目录配置。两个配置都以FPM进程实际加载的生效值为准,不要只参考CLI模式下读取到的配置结果。

move_uploaded_file 执行成功后还需要手动调用 unlink 删除吗?

通常不需要。文件已经被移动到新路径,原来的临时路径下已经不存在对应文件,重复执行删除只会打满无意义的错误日志。只有异常分支场景下,才需要确认路径确实属于上传临时目录后再做清理。

可以每分钟跑个定时任务直接清空 upload_tmp_dir 目录吗?

不建议这么做。间隔过短的清理任务很容易删掉还在传输中的慢上传文件。要先设置明确的文件年龄阈值、先观察全量活跃请求状态、配置好目录白名单,预览完待清理列表再执行实际删除操作。

小结

PHP-FPM上传临时文件堆积,通常不是单独的“清理脚本出问题”,而是文件接收、校验、移动和异常回收整条链路里缺了一段可靠的交接逻辑。先定位进程实际在用的临时目录,再让成功路径和失败路径都能输出明确的状态,最后用超期文件兜底清理 + 文件数/字节数双维度告警做收口,线上处理这类问题才不会一直停留在出事就删文件的层面。

[] []
版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Redis 过期键为什么没有按时删除:惰性删除、定期扫描与通知验证Redis 过期键为什么没有按时删除:惰性删除、定期扫描与通知验证
上一篇
Redis 过期键为什么没有按时删除:惰性删除、定期扫描与通知验证
Go 1.25 容器里的 GOMAXPROCS 怎么迁移:cgroup CPU 限额、自动更新与旧环境兼容
下一篇
Go 1.25 容器里的 GOMAXPROCS 怎么迁移:cgroup CPU 限额、自动更新与旧环境兼容
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • ljg-skills -
    ljg-skills
    ljg-skills 是李继刚开源的 AI 技能与提示词集合,面向大模型使用者整理了一批可复用的 prompt、角色设定和任务技能模板,适合用于学习提示词设计、搭建个人 AI 工作流和沉淀团队常用智能体能力。
    4797次使用
  • MELO音乐 - AI 音乐生成平台,支持多模态创作能力
    MELO音乐
    MELO音乐是一站式AI视频与音乐制作助手,对标suno, udio的高品质体验。提供伴奏生成、原创写词、无损导出、哼唱识曲、混音变声等全套音频与短视频编辑工具。无论是流行Kpop、电音说唱、民谣古风、摇滚儿歌还是商用轻音乐,MELO为你免费谱曲,轻松做同款!
    4389次使用
  • UniScribe - AI 免费在线音视频转文字平台
    UniScribe
    UniScribe 是一款 AI 音视频转文字与内容整理工具,支持上传音频、视频文件或粘贴 YouTube 链接,自动生成转写文本、摘要、思维导图和关键问题,并支持多格式导出,适合会议记录、课程学习、访谈整理和内容创作复盘。
    4334次使用
  • 剧云 - 免费 AI 智能中文剧本创作平台
    剧云
    剧云是专业中文剧本创作平台,安全稳定运行十余年,集成AI编剧、剧本医生审核、人物小传、剧情关系图、大纲编写、多人协作、Word导入导出、版权管控功能,数据安全防护,轻松高效创作剧本。
    4571次使用
  • 万象有声 - AI 一站式有声内容创作平台
    万象有声
    万象有声,一个专为有声创作者打造的新一代智能有声内容创作平台。平台提供专业的智能拆章、智能画本编辑、AI配音、AI生成音效、后期制作、智能对轨、智能审听等有声创作全流程工具,可以帮助创作者高效、低成本创作出引人入胜的有声作品。立即体验,让有声书制作更简单!
    4515次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码