php侧必须用php://input流式接收或前端分片上传,不能依赖$_files(仅上传完成时填充);进度条需用xmlhttprequest.upload.onprogress监听,后端用redis缓存分片进度并提供独立查询接口。

PHP 侧要用 $_FILES 还是 $_POST 接收上传数据?
进度条依赖分块上传或服务端状态暴露,不能靠 $_FILES 一次性接收——它只在上传完成时才填充,根本看不到中间过程。必须改用流式接收或配合前端分片上传,让 PHP 能在上传中就拿到部分数据并记录进度。
常见错误是直接写 move_uploaded_file($_FILES['file']['tmp_name'], ...),这会导致整个上传结束前毫无状态可查,进度条永远卡在 0%。
- ThinkPHP 6+ 默认禁用
php://input流读取(因与$_POST/$_FILES冲突),需在public/index.php开头加if (isset($_SERVER['CONTENT_TYPE']) && strpos($_SERVER['CONTENT_TYPE'], 'multipart/form-data') === false) { $input = file_get_contents('php://input'); }才能手动解析原始 body - 更稳妥的做法是前端用
XMLHttpRequest分片上传,每片发一个POST /upload/chunk请求,服务端用md5或chunk_index标识并累加已收大小,存到cache或临时文件里 - 别依赖
upload_progress机制(如 APC、UploadProgress 扩展),ThinkPHP 不内置支持,且 PHP 7.4+ 已移除对 APC 的兼容,线上环境大概率不可用
前端怎么监听真实上传进度而不是“假进度”?
用 XMLHttpRequest.upload.onprogress 是唯一可靠方式;fetch 没有原生上传进度事件,强行封装容易丢帧或不准。
典型错误是用定时器模拟进度(比如 1s +10%),实际网络卡顿时用户看到进度条跑完了但接口还没返回,体验崩坏。
- 必须设置
xhr.upload.onprogress = function(e) { if (e.lengthComputable) { console.log(e.loaded / e.total); } },否则e.loaded和e.total都是 0 - 后端响应头要带
Access-Control-Allow-Headers: X-Requested-With,否则跨域时onprogress不触发 - 大文件建议加断点续传:前端传
file_id+chunk_index,后端校验该分片是否已存在,避免重复写入
ThinkPHP 怎么安全地暴露进度查询接口?
不能把进度存在 session 或全局变量里——多服务器部署时会不一致;也不能每次查都扫临时目录——IO 开销大且不安全。
前端设计与 UI/UX 全方位优化专家。覆盖视觉层次、排版系统、色彩理论、响应式布局、交互体验、动画动效、无障碍访问、性能优化八大维度,帮助开发者将普通页面升级为高品质产品级界面。前端设计与 UI/UX 全方位优化专家。覆盖视觉层次、排版系统、色彩理论、响应式布局、交互体验、动画动效、无障碍访问、性能优化八大维度,帮助开发者将普通页面升级为高品质产品级界面。
最简方案是用 ThinkPHP 自带的 cache 驱动(Redis 最佳),key 设为 upload_progress_{file_id},值为 JSON 字符串 {"total":10485760,"uploaded":3215680}。
- 接口路由建议设为
GET /api/progress?file_id=abc123,控制器里用cache('upload_progress_'.$file_id)读取,不存在就返回404 - 注意清理:上传完成或失败后主动删掉 cache key,否则垃圾数据越积越多;可用
cache(..., null, 3600)设自动过期,但别超过 24 小时 - 别把
file_id直接暴露成用户可控路径(如/progress/{file_id}),防止恶意刷 key 扫描,加一层简单校验,比如strlen($file_id) === 32 && ctype_xdigit($file_id)
为什么上传完成时进度条总卡在 99%?
这是前后端状态不同步的典型表现:前端认为“上传完成”是指 xhr.upload.onload 触发,但后端可能还在处理(压缩、转码、写数据库),此时查进度接口仍返回 99%,直到处理完才更新到 100%。
更糟的是,有些同学把“上传完成”和“业务处理完成”混为一谈,导致用户点了上传按钮,进度条走到头却没反应,以为卡死了。
- 解决方案是拆成两个状态:前端显示“上传中 → 上传完成 → 处理中 → 完成”,后端提供两个接口:
/api/progress(仅上传进度)和/api/task_status(业务处理状态) - ThinkPHP 中,上传完成后应立即异步触发耗时操作,比如用
think\queue\Job投递队列任务,同时立刻返回{"status":"uploaded","file_id":"..."},让前端切换到“处理中”态 - 别在上传接口里调
sleep(2)模拟处理——这会阻塞 PHP-FPM 进程,高并发下直接拖垮服务
进度条不是炫技功能,本质是降低用户不确定性焦虑。真正难的不是怎么画那个条,而是让每个环节的状态定义清晰、传递及时、清理干净。漏掉任意一环,用户看到的就是“好像好了又好像没好”。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!










