
当用户需要上传大文件时,直接上传容易因超时、内存溢出或网络中断导致失败。PHP分片上传通过将文件切分为多个小块分别上传,再在服务端合并,提升上传稳定性与成功率。以下是实现该功能的具体方法:
一、前端分片与断点续传控制
前端需使用JavaScript读取文件并按固定大小(如2MB)切片,为每一片生成唯一标识(如文件名+分片序号+总片数),同时记录已上传分片索引,支持断点续传。上传请求携带分片元数据,便于后端校验与合并。
1、使用File API读取用户选择的文件对象,调用slice()方法按指定字节范围截取分片。
2、为每个分片构造FormData对象,附加file(Blob)、filename、chunkIndex、totalChunks、fileHash等字段。
立即学习“PHP免费学习笔记(深入)”;
3、使用fetch或XMLHttpRequest逐个发送分片,设置超时与重试机制,跳过已成功响应的分片索引。
4、所有分片上传完成后,发起合并请求,提交fileHash和filename供服务端校验完整性。
二、服务端分片接收与临时存储
PHP脚本接收单个分片后,不直接写入最终文件,而是保存为带唯一标识的临时文件(如upload_{fileHash}_{chunkIndex}),避免命名冲突与并发覆盖,并记录各分片状态到本地文件或数据库。
1、通过$_FILES[‘file’][‘tmp_name’]获取上传分片临时路径。
2、从POST参数中提取fileHash、chunkIndex、totalChunks、filename,拼接临时文件名:upload_{$fileHash}_{$chunkIndex}。
3、使用move_uploaded_file()将分片移至指定临时目录(如./uploads/chunks/)。
4、将当前分片索引写入状态文件(如./uploads/status/{$fileHash}.json),或插入mysql表记录uploaded_chunks字段值。
三、服务端合并逻辑与完整性校验
合并操作需确认所有分片均已到达,按序号升序读取临时分片文件,追加写入目标文件;完成后校验合并后文件的MD5或SHA256是否与前端传递的fileHash一致,防止数据损坏。
1、根据fileHash查找状态文件或数据库记录,确认uploaded_chunks数组长度等于totalChunks。
2、初始化目标文件句柄(fopen(“./uploads/final/{$filename}”, “wb”)),按chunkIndex从小到大遍历临时分片路径。
3、对每个分片调用fopen()读取内容,使用fpassthru()或stream_copy_to_stream()追加写入目标文件句柄。
4、关闭所有句柄后,执行hash_equals($expected_hash, hash_file(‘sha256’, $final_path))验证一致性,失败则删除目标文件并返回错误。
四、分片清理与异常处理
合并成功后需及时清理临时分片及状态记录,防止磁盘空间持续增长;若合并失败或长时间未完成,应设置定时任务扫描过期分片(如创建时间超过24小时)并清除。
1、合并成功后,遍历./uploads/chunks/目录下所有匹配upload_{$fileHash}_*的文件,执行unlink()删除。
2、删除对应的状态文件./uploads/status/{$fileHash}.json或清空数据库中该fileHash的记录。
3、在合并入口脚本顶部添加set_time_limit(0)和ignore_user_abort(true),防止超时中断。
4、所有文件操作前均需校验is_uploaded_file($_FILES[‘file’][‘tmp_name’]),杜绝恶意路径注入。
五、Nginx配置优化与超时适配
默认Nginx限制单次请求体大小与超时时间,需调整以适配分片上传场景,确保单个分片能顺利抵达PHP,且长连接不被代理中断。
1、在server或location块中设置client_max_body_size 100M,允许单个分片最大体积(需大于分片尺寸)。
2、配置client_body_timeout 600与send_timeout 600,延长请求体接收与响应发送超时。
3、添加proxy_read_timeout 600(如使用反向代理),避免上游等待超时断开。
4、禁用请求体缓冲:client_body_buffer_size 128k,配合client_body_in_single_buffer on提升分片写入效率。