Laravel大文件上传易爆内存或500错误,因默认走PHP原生流程,需完整加载文件至内存;应改用分片上传,前端用Resumable.js或fetch分片,后端按hash标识存临时分片、异步合并并清理。

为什么 $_FILES 直接爆内存或 500?
因为 Laravel 默认走 PHP 原生上传流程,大文件(比如 >10MB)会先完整写入临时目录,再由框架读取解析 —— 这期间 PHP 进程要 hold 住整个文件内容,容易触发 memory_limit 或超时。Nginx/Apache 层也可能直接拦截(如 Nginx 的 client_max_body_size)。不是 Laravel 慢,是它没被设计成“扛大文件”的第一道门。
- 检查
php.ini:确保upload_max_filesize和post_max_size都调大(比如128M),但别指望靠这个解决 500MB 级上传 - Nginx 必须同步配
client_max_body_size 128m,否则请求根本到不了 PHP - 哪怕参数全开,单次上传仍受
max_execution_time限制,用户等 3 分钟大概率点刷新
用 laravel-chunk-upload 还是手撸 Resumable.js + 自定义接口?
优先选轻量可控的手动方案。第三方包如 laravel-chunk-upload 封装了分片逻辑,但隐藏了关键细节:比如怎么校验分片顺序、如何防重复提交、断点续传时怎么查已传块。一旦出问题,你得钻进包里 debug;而自己控制上传协议,能精准应对业务约束(比如只允许上传视频、需绑定用户 ID、上传中实时更新进度)。
- 前端用
Resumable.js或原生fetch分片(每片 5MB),带上identifier(如文件 hash)、filename、chunkNumber、totalChunks - Laravel 接口接收分片:存到
storage/app/chunks/{identifier}/,文件名用{chunkNumber}.part - 合并时机不在上传完成立刻触发,而是等客户端发
/api/merge?identifier=xxx—— 这样可加权限校验、异步队列处理
Storage::put() 合并大文件会卡住?
会。直接用 Storage::put('video.mp4', $content) 把几百 MB 拼好的二进制塞进去,PHP 进程又得全 load 到内存。正确做法是让系统命令干这事,Laravel 只调度。
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
- 用
file_put_contents($finalPath, file_get_contents($chunkPath), FILE_APPEND | LOCK_EX)逐片追加,不占 PHP 内存 - 更稳的是 shell 命令:
cat storage/app/chunks/abc/*.part > storage/app/uploads/abc_final.mp4,交由 OS 处理 I/O - 合并完立刻删分片目录,不然磁盘悄悄爆掉
- 注意
chmod权限:Web 用户(如 www-data)必须对chunks/和uploads/有读写权
前端传 identifier 用文件名还是 hash?
必须用 hash(比如 sha256(file.name + file.size + Date.now()))。用原始文件名会导致冲突:用户 A 传 report.pdf,用户 B 几秒后也传同名文件,后者的分片会覆盖前者的临时目录,合并时文件就乱了。
- hash 要在前端算,不能依赖后端生成 —— 否则无法支持断点续传(客户端需凭同一 identifier 续传)
- 别用
Math.random()或时间戳当 identifier,碰撞概率高,尤其并发上传时 - 把 hash 存进数据库临时表(带
user_id、status、expires_at),既防恶意刷接口,也方便后台清理过期分片
真正麻烦的从来不是“怎么拼起来”,而是“怎么确认每一片都到了、没重复、没篡改、超时了怎么清”。分片只是手段,状态管理才是核心。










