分片上传是唯一靠谱方案,需前端切片、后端逐片落盘合并,禁用整文件读取,同步调优Nginx、PHP-FPM及PHP配置。

别关 memory_limit,也别指望 ini_set('memory_limit', '-1') 能救大文件上传——它只会让 PHP 进程被系统 OOM Killer 杀掉,或者卡死在 Nginx 层。
为什么 upload_max_filesize 调大了还是报内存溢出
常见错误现象是:前端传一个 500MB 文件,PHP 报 Fatal error: Allowed memory size of 134217728 bytes exhausted,但你明明已把 upload_max_filesize 和 post_max_size 都设成 1G。
- Nginx 默认
client_max_body_size是 1M,根本拦在 PHP 之前,连请求都进不来 - PHP 接收上传时,
$_FILES['file']['tmp_name']虽然是临时路径,但若后续用file_get_contents($_FILES['file']['tmp_name'])读取,就会把整个文件加载进内存 -
max_execution_time很可能先超时,尤其在慢网或高负载时,和内存无关却表现为“卡住” - 某些托管环境(如 cPanel 共享主机)会静默忽略
ini_set()对memory_limit的修改
分片上传是唯一靠谱的落地方案
必须从前端切片开始,后端只接收、校验、合并,不整包加载。
- 前端用
Blob.slice()切,每片建议 2–5MB;带上file_id、chunk_index、total_chunks和单片md5 - 后端用
move_uploaded_file()直接落盘到临时目录(如/tmp/uploads/{file_id}/chunk_001.bin),绝不用file_get_contents() - 合并时用
fopen($final, 'wb')+fwrite()逐片追加,不要拼字符串再file_put_contents() - 合并完成后调用
hash_file('sha256', $final)校验总哈希,匹配前端传来的完整 hash - 无论成功失败,都要清理临时分片目录;可用
register_shutdown_function()做兜底,或交由队列异步清理
PHP 层必须同步调整的配置项
光改 PHP 不行,Nginx 和 PHP-FPM 必须协同放开限制并禁用缓冲干扰:
立即学习“PHP免费学习笔记(深入)”;
- Nginx 配置中加:
client_max_body_size 2G;、client_body_buffer_size 128k;、client_body_timeout 300; - 禁用代理缓冲:
proxy_buffering off;、fastcgi_buffering off;(PHP-FPM 模式下) - PHP-FPM pool 配置里调高:
request_terminate_timeout = 300、request_slowlog_timeout = 60 - PHP 脚本内可临时放宽(仅限上传接收逻辑):
ini_set('memory_limit', '512M');,但绝不设为-1
流式处理替代整文件读取的实操写法
哪怕只是做上传后的预检(比如读前 1KB 看文件头),也该避免 file_get_contents():
- 检测 CSV 头部字段:
$fp = fopen($_FILES['file']['tmp_name'], 'r'); $header = fgetcsv($fp); fclose($fp); - 读取图片尺寸:
getimagesizefromstring(file_get_contents($path, false, null, 0, 2048))→ 改用fopen()+fread($fp, 2048)再传给getimagesizefromstring() - 日志分析类场景:用
SplFileObject迭代,$file->seek(100000)跳过前 10 万行,再foreach ($file as $line),不缓存全文
真正棘手的不是“怎么撑住”,而是“怎么让每一步都不依赖整块内存”——分片逻辑要贯穿前后端,临时文件路径要防冲突,校验要落在字节级,否则看似传过去了,实际数据已损坏或不完整。



















