当前位置:首页 > 文章列表 > 文章 > php教程 > PHP 表单上传大文件怎么做断点续传:分片校验、临时文件与合并

PHP 表单上传大文件怎么做断点续传:分片校验、临时文件与合并

来源:17golang原创 2026-08-25 01:13:05 0浏览 收藏

大文件上传最麻烦的地方不是把一个 $_FILES 写进磁盘,而是用户传到 80% 时网络断了,下一次打开页面还得从头来。更稳妥的做法是把文件拆成固定大小的分片,每片单独校验并落到任务目录,服务端只在所有分片齐全且顺序正确时合并成最终文件。

断点续传的核心不是“记住上传进度”,而是让客户端能查询已完成分片,并让服务端用文件哈希、分片编号和临时目录把一次上传任务区分开。

要点速览:
  • 用 upload_id 隔离每一次上传任务。
  • 分片写入临时目录并在改名后返回成功。
  • 合并前检查分片总数,完成后核对整文件哈希。
  • 失败任务保留状态并按规则清理临时文件。

先把上传协议定清楚

建议把一次上传抽象成三个接口:创建任务、上传分片、查询与合并。客户端第一次提交文件名、文件总大小、总分片数和整文件哈希,服务端返回不可猜测的 upload_id。后续请求只携带这个 ID、分片编号、分片总数和当前二进制内容。

分片编号从 0 开始,服务端文件名统一使用 000000.part 这样的格式,不直接相信客户端传来的原始文件名。原始文件名只作为展示字段,最终保存名应由服务端生成。

POST /upload/init
{
  "name": "backup.zip",
  "size": 524288000,
  "parts": 50,
  "sha256": "整文件哈希"
}

PHP 接收分片时先做边界校验

上传接口收到请求后,第一件事是检查任务是否存在、分片编号是否在范围内、分片大小是否超过上限。不要把用户传来的文件名直接拼进路径,也不要用“文件存在就算成功”的判断,因为上次中断可能留下半截文件。

= $total) {
    http_response_code(422);
    exit('invalid part metadata');
}

$root = __DIR__ . '/runtime/uploads/' . basename($uploadId);
if (!is_dir($root) && !mkdir($root, 0700, true)) {
    throw new RuntimeException('cannot create upload directory');
}
$target = $root . '/' . sprintf('%06d.part', $part);
$input = fopen('php://input', 'rb');
$output = fopen($target . '.tmp', 'wb');
stream_copy_to_stream($input, $output);
fclose($input);
fclose($output);
rename($target . '.tmp', $target);
echo json_encode(['ok' => true, 'part' => $part]);

示例中的临时文件和改名动作很重要:合并逻辑永远只读取已经改名完成的 .part 文件,不会碰到正在写入的临时文件。生产环境还应限制任务目录的属主和访问权限,并把上传 ID 与登录用户绑定。

查询接口要返回“已完成哪些片”

客户端重试前先调用查询接口。服务端扫描当前任务文件夹,只认符合六位数字编号的分片,并返回编号数组、总分片数和任务状态。不要只返回一个百分比,百分比无法告诉客户端缺的是哪一片。

$done = [];
foreach (glob($root . '/*.part') as $file) {
    if (preg_match('/\/(\d{6})\.part$/', $file, $m)) {
        $done[] = (int) $m[1];
    }
}
sort($done);
echo json_encode(['parts' => $done, 'status' => 'uploading']);

如果查询结果中已经有当前分片,客户端可以跳过它;如果只有临时文件,没有对应的 .part 文件,就继续上传。这样即使浏览器刷新,也不会把“正在写入”误当成成功。

合并前后分别做一次检查

合并接口不能只检查目录里有多少文件。它应该按 0 到 total - 1 逐片打开,确认每个分片存在,再使用二进制方式追加到一个新的临时目标文件。任何一片缺失都返回可重试的状态,而不是生成一个看似完整的坏文件。

$mergedTmp = $root . '/result.tmp';
$out = fopen($mergedTmp, 'wb');
for ($i = 0; $i 

合并完成后再计算最终文件的 SHA-256,并与初始化时记录的值比较。哈希不一致时,删除最终文件并把任务标记为失败,保留缺片信息供客户端重新上传。不要因为 HTTP 200 就认为文件可用。

错误码要让调用方知道下一步

上传接口可以用 422 表示元数据错误,用 404 表示任务不存在,用 409 表示分片缺失或任务状态冲突,用 507 表示磁盘空间不足。响应体再补一个稳定的业务字段,例如 part_missingchecksum_mismatchuploading,前端就能区分“重新传这一片”和“重新创建任务”。

重试也要有边界:同一分片可以重传,合并请求可以带幂等键,但不要在服务端无限重试文件操作。磁盘写满、权限错误和哈希不一致都需要留下日志,方便定位。

清理策略决定了断点续传能否长期运行

临时目录不能永久保留。初始化任务时记录 updated_at,每次成功写片或查询时更新它;定时清理脚本只删除超过保留时间、且不在合并状态的任务。清理前再次读取状态,避免和合并请求同时操作同一目录。

最终文件写入完成后,删除分片和任务元数据,只保留结果文件、整文件哈希和必要的审计信息。用户取消上传时也走同一套清理逻辑,不要把删除路径交给客户端拼接。

上线前用四种中断场景验收

先传完前 30% 后断网,刷新页面,确认查询接口能返回已完成分片;再重复上传一片,确认不会产生重复内容;随后人为删除一片再调用合并,确认返回 409 且没有残留结果;最后修改一个分片内容,确认整文件哈希校验失败。

还要检查 PHP 的 upload_max_filesizepost_max_size、反向代理请求体限制和磁盘配额。分片大小即使低于 PHP 限制,也可能被 Nginx 或网关拦截,验收不能只在本机完成。

相关问题

分片大小应该固定吗?

第一版建议固定在 5MB 到 20MB 之间,并根据移动网络和网关限制调整。固定大小更容易计算编号、查询缺片和重试,最后一片可以小于标准大小。

为什么不直接用原始文件名保存?

原始文件名可能包含路径片段、特殊字符或同名冲突。使用服务端生成的 upload_id 和最终文件名,能把展示信息与存储路径分开。

合并接口需要加锁吗?

需要保证同一任务不会同时合并。可以用数据库状态从 uploading 原子切换到 merging,并让重复请求读取已有结果;不要只依靠目录里是否存在 result.tmp。

把“能续传”落实成可验证的状态机

一套可靠的 PHP 断点续传流程,至少要让创建、分片写入、查询、合并、校验和清理这些动作都有明确状态。客户端负责记录 upload_id,服务端负责验证每片和最终结果。只要先把协议和失败边界定下来,后续替换前端组件或存储介质,也不会重新陷入“断了就从头传”的问题。

PHP 大文件断点续传中分片编号、校验和服务端临时目录的对应关系

PHP 断点续传合并前检查缺片、合并后整文件校验与失败清理流程

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go 1.27 uuid 包怎么选:从自定义实现迁移时要核对什么Go 1.27 uuid 包怎么选:从自定义实现迁移时要核对什么
上一篇
Go 1.27 uuid 包怎么选:从自定义实现迁移时要核对什么
Java StringJoiner 怎么拼带前后缀的列表:空集合、嵌套分隔和流式收集
下一篇
Java StringJoiner 怎么拼带前后缀的列表:空集合、嵌套分隔和流式收集
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之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 工作流和沉淀团队常用智能体能力。
    5228次使用
  • MELO音乐 - AI 音乐生成平台,支持多模态创作能力
    MELO音乐
    MELO音乐是一站式AI视频与音乐制作助手,对标suno, udio的高品质体验。提供伴奏生成、原创写词、无损导出、哼唱识曲、混音变声等全套音频与短视频编辑工具。无论是流行Kpop、电音说唱、民谣古风、摇滚儿歌还是商用轻音乐,MELO为你免费谱曲,轻松做同款!
    4737次使用
  • UniScribe - AI 免费在线音视频转文字平台
    UniScribe
    UniScribe 是一款 AI 音视频转文字与内容整理工具,支持上传音频、视频文件或粘贴 YouTube 链接,自动生成转写文本、摘要、思维导图和关键问题,并支持多格式导出,适合会议记录、课程学习、访谈整理和内容创作复盘。
    4686次使用
  • 剧云 - 免费 AI 智能中文剧本创作平台
    剧云
    剧云是专业中文剧本创作平台,安全稳定运行十余年,集成AI编剧、剧本医生审核、人物小传、剧情关系图、大纲编写、多人协作、Word导入导出、版权管控功能,数据安全防护,轻松高效创作剧本。
    4943次使用
  • 万象有声 - AI 一站式有声内容创作平台
    万象有声
    万象有声,一个专为有声创作者打造的新一代智能有声内容创作平台。平台提供专业的智能拆章、智能画本编辑、AI配音、AI生成音效、后期制作、智能对轨、智能审听等有声创作全流程工具,可以帮助创作者高效、低成本创作出引人入胜的有声作品。立即体验,让有声书制作更简单!
    4900次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码