分片上传+服务端安全合并是可靠支持大文件上传的唯一方案;需同步调整Nginx(client_max_body_size、超时)、PHP(upload_tmp_dir、校验逻辑)及前端(Blob.slice分片、MD5预计算、元数据传递)。

单纯调大 upload_max_filesize 无法可靠支持大文件上传——它只是第一道门,后面还卡着 Nginx、PHP 超时、内存、临时文件生命周期和校验逻辑。真正能落地的方案,是分片上传 + 服务端安全合并。
为什么改了 php.ini 还传不过 100MB 文件
Nginx 拦在最外层:client_max_body_size 默认是 1M,必须同步设为 ≥ 你期望的上传上限(如 2G),否则请求根本进不了 PHP;client_body_timeout 和 proxy_read_timeout(若走反代)必须设到 600 秒以上,否则 Nginx 主动断连;max_execution_time 只管脚本执行,不管 HTTP 传输耗时,上传中脚本可能早于文件收完就被 kill,得配合 set_time_limit(0) 临时关掉;前端若仍用原生 <input type="file"> 提交,浏览器会把整个文件塞进单次 POST,50MB+ 就极不稳定,且无法断点续传。
前端必须用 Blob.slice() 分片,不能靠表单 submit
原生表单提交是整包发送,无法控制分片逻辑。必须用 JavaScript 手动切片:
const file = input.files[0];
const chunkSize = 5 * 1024 * 1024; // 5MB
for (let start = 0; start < file.size; start += chunkSize) {
const blob = file.slice(start, start + chunkSize);
const formData = new FormData();
formData.append('file', blob);
formData.append('chunk_index', Math.floor(start / chunkSize));
formData.append('total_chunks', Math.ceil(file.size / chunkSize));
formData.append('file_id', 'abc123'); // 唯一标识,建议用 UUID 或文件哈希
// 发送请求...
}关键点:
立即学习“PHP免费学习笔记(深入)”;
– 每个请求必须带 file_id、chunk_index 和 total_chunks,缺一不可;
– 推荐用 HTTP Header(如 X-Upload-ID)传元数据,比塞在 FormData 里更干净、不易被编码污染;
– 前端应预计算每片的 md5 并通过 Header 或字段传给后端,用于接收时校验。
PHP 接收分片时怎么避免 move_uploaded_file() 失败
失败主因不是“文件太大”,而是临时文件生命周期失控或路径权限错位:
– 别依赖系统默认 upload_tmp_dir(通常是 /tmp),那里文件可能被 cron 清理或过期;显式在 php.ini 中设成可控路径,如 upload_tmp_dir = /var/www/uploads/tmp,并确保 Web 用户(如 www-data)有读写权限;
– move_uploaded_file() 要求源路径有效且未被销毁,而分片跨秒/跨请求,临时文件极易失效;更稳妥的是用 file_get_contents($_FILES['file']['tmp_name']) 读取后直接写入自定义命名的分片文件(如 abc123_0.bin);
– 接收时务必检查 $_FILES['file']['error'] === UPLOAD_ERR_OK,非此值说明上传中途被 Nginx 截断、客户端中断或磁盘满,不能继续后续逻辑;
– 分片文件名必须含唯一标识(如 file_id)和序号(如 chunk_index),避免不同上传任务写混。
合并分片前必须做的三重校验
收到全部分片 ≠ 文件完整。跳过校验直接拼接,线上容易出现“上传成功但解压报错”“视频播放花屏”等隐蔽故障:
– 数量校验:用 scandir() 扫描该 file_id 下所有 *.bin 文件,确认数量等于前端传的 total_chunks;
– 内容校验:每个分片接收后立刻计算 md5_file($_FILES['file']['tmp_name']),与前端传的 X-Chunk-Hash 比对;不一致则拒绝;
– 顺序与原子性校验:合并时用 fopen($final, 'wb') 创建新文件,再逐块 fwrite(),而非 fopen(..., 'ab') 追加——后者在并发场景下易乱序或覆盖;合并完成后再用 rename() 原子替换目标文件,避免中间态被访问。
临时目录权限、分片命名冲突、Nginx 超时参数遗漏、前端未做 chunk hash 预计算——这些点任何一个没对齐,都会让看似跑通的分片上传在线上静默失败。



















