php断点续传核心是分片上传+服务端拼接+状态记录,需前端切片并传filename/chunkindex/totalchunks/identifier,服务端校验分片、存临时文件、记录json状态、合并后md5校验。

PHP如何接收分片并校验文件完整性
断点续传的核心不是“续传”,而是“分片上传 + 服务端拼接 + 断点状态记录”。PHP本身不处理HTTP断点(Range请求),而是配合前端分片上传逻辑,接收每个chunk,并验证是否已存在、是否完整。
常见错误是直接用$_FILES['file']接收整个大文件——这会失败或超时。必须让前端按固定大小(如5MB)切片,每片带参数:filename、chunkIndex、totalChunks、identifier(唯一ID,建议用文件名+大小+md5前8位生成)。
- 服务端先检查
identifier/chunkIndex对应文件是否已存在(避免重复写入) - 用
move_uploaded_file()存到临时目录,路径建议为uploads/{identifier}/{chunkIndex} - 收到最后一片后,按
chunkIndex顺序合并:file_put_contents($final, file_get_contents($chunk), FILE_APPEND) - 合并前建议对每个分片做
sha256_file()校验(若前端也传了分片hash),否则中间损坏无法发现
怎么保存和读取断点上传状态
断点依赖可持久化的上传上下文。不能只靠session(重启丢失、多机不共享),也不能硬编码到文件名里(不安全)。最简方案是用一个轻量级状态文件或数据库记录。
推荐用JSON文件存状态,路径为uploads/{identifier}/status.json,内容示例:
{
"filename": "report.pdf",
"total_chunks": 12,
"uploaded_chunks": [0, 1, 2, 4, 5],
"upload_started_at": 1717023456,
"expires_at": 1717030656
}
每次上传分片时:file_get_contents()读状态 → 检查chunkIndex是否已在uploaded_chunks中 → 若未存在,追加进数组并file_put_contents()回写。
- 务必加
flock()防止并发写冲突:fopen(..., 'c')+flock($fp, LOCK_EX) - 设置过期时间(如2小时),避免死状态堆积;前端可定时GET
/api/upload/status?identifier=xxx来拉取已传分片列表 - 不要把敏感信息(如用户ID)直接写进
identifier,建议用服务端生成的UUID映射关系
为什么不能直接用$_SERVER['HTTP_RANGE']实现断点下载
你可能看到过“PHP支持断点下载”的例子,但那只是响应Range请求返回文件片段,和“上传断点”完全无关。上传过程是客户端主动推送数据,服务端无权要求客户端“从第X字节继续发”——HTTP协议不支持上传侧的Range语义。
所有真正可用的断点续传,底层都靠前端控制分片、记录已传索引、失败后重传指定chunkIndex。PHP只需做三件事:存分片、记状态、拼文件。
-
$_SERVER['HTTP_RANGE']只在GET请求中有效,且仅用于下载场景 - 试图在
POST中解析Range头是无效的,Web服务器(如Nginx)通常会忽略或报错 - 如果前端没做分片逻辑,只传一个大文件,PHP再怎么写也“续”不起来
合并文件后要不要重新计算MD5
要,而且必须在合并完成后立即算,不能依赖分片hash累加。因为分片传输中可能被篡改、磁盘写入出错、或合并时FILE_APPEND因权限问题静默失败。
- 用
md5_file($final_path)比逐片md5()拼接更可靠,开销也不高(PHP 8.1+对大文件做了流式优化) - 把这个最终MD5和前端初始化时传来的
fileMd5比对,一致才返回{"status":"success"} - 若不一致,删掉
$final_path和所有分片,清空status.json,让前端从头开始(或提示“校验失败,请重试”) - 别省略这步——线上曾有案例因NFS挂载延迟导致
file_put_contents写入不全,跳过校验就发了损坏文件
真正的难点不在PHP代码几行,而在于前后端对“分片序号”“唯一标识”“失败重试策略”的严格对齐。少一个字段约定,就卡在某个chunkIndex反复上传不成功。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











