【一个PHP外包仔的2G文件上传血泪史从WebUploader到自救之路】老板这个2G的文件上传需求…要不咱们先聊聊预算我擦着额头的汗盯着客户发来的需求文档。作为一个月接3个外包的野生PHP工程师我深知这次要栽——毕竟上次用WebUploader上传200M的PPT客户的Chrome就崩溃成了PPT字面意思。第一幕WebUploader的临终遗言兄弟我尽力了。看着控制台里第18个502错误WebUploader的JS文件仿佛在对我鞠躬谢罪。这个百度开源的组件就像个倔强的老黄牛分片上传能工作但偶尔会漏掉第7片就像我小时候抄作业总会漏几行进度条在IE11里会表演量子波动前进断点续传客户重启路由器后所有分片都会集体玩失踪最绝的是技术支持“亲建议升级到最新版哦~”——可最新版已经是5年前的遗孤啊第二幕前端后端的相爱相杀Vue那边说需要MD5校验分片我冲着电话大喊嘴里还塞着煎饼果子。前端小哥的声线透着绝望“哥WebUploader的文档比你的PHP注释还难懂…”于是我们开启了跨服聊天模式// 前端发来的神秘代码片段uploader.on(uploadError,function(file){console.log(出错了但我不知道错哪了);// 实际应该触发PHP端的日志接口但没人记得写});// 我的后端回应喝到第三罐红牛后的产物$chunk$_FILES[file]??null;if(magic_check_chunk($chunk)){// 这个函数名就透着玄学move_uploaded_file(...);// 偶尔会移动到虚无空间echo{success:true};// 其实可能没成功}第三幕MySQL的无声抗议当客户问能不能显示所有上传任务的历史记录时我盯着InnoDB表陷入了沉思CREATETABLEupload_tasks(idBIGINTAUTO_INCREMENT,file_hashVARCHAR(64),-- 存储整个文件的MD5total_sizeBIGINT,-- 2G文件就是2147483648statusTINYINT,-- 0上传中 1完成 2失败但可能部分分片成功-- 省略了5个关联表的设计...);直到测试时发现插入2000条记录后查询进行中的任务需要3.7秒文件哈希列没有索引别问我怎么知道的当Nginx超时断开连接时PHP还在傻乎乎地插入分片记录第四幕绝地求生方案经过三天三夜的谷歌搜索和两包速溶咖啡我制定了新方案前端改造计划弃用WebUploader改用resumable.js至少文档是2023年的实现真正的断点续传// 伪代码用localStorage存储已上传分片constuploadedChunksJSON.parse(localStorage.getItem(upload_progress_file.id))||[];添加心跳检测防止浏览器假死后端自救指南用Swoole替代传统PHP-FPM虽然还在学但看文档说能处理长连接分片存储方案// 每个分片单独存储成功后再合并$chunkDir/tmp/uploads/.$fileHash;if(!is_dir($chunkDir))mkdir($chunkDir,0777,true);move_uploaded_file($_FILES[file][tmp_name],$chunkDir/$chunkIndex);合并时用exec(cat /tmp/uploads/* final.mp4)虽然不优雅但快数据库优化改用MongoDB存储分片元数据终于不用纠结外键了添加Redis缓存当前上传任务对大文件记录采用软删除策略标记为删除但不真删怕客户反悔终幕测试日的疯狂当客户终于发来测试文件时我的监控面板是这样的Nginx错误日志每分钟新增3条upstream timed outPHP-FPM内存占用突破1.2GMySQL慢查询日志里全是SELECT * FROM upload_tasks但当那个2.3G的《复仇者联盟4》完整版终于显示上传成功时我激动得把咖啡打翻在键盘上——至少这次没把服务器宕机对吧客户反馈IE11下进度条会跳芭蕾舞。我微笑.jpg安装环境PHP:7.2.14调整块大小NOSQLNOSQL不需要任何配置可以直接访问测试SQL创建数据库您可以直接复制脚本进行创建配置数据库连接安装依赖访问页面进行测试数据表中的数据免费下载示例点击下载完整示例