上周三下午,文件位置运维群里炸了锅。上传一位同事上传产品手册到后台,指定进度条卡在99%,异常刷新页面后文件没了,文件位置服务器上对应的上传目录里只有个0字节的临时文件。他试了三次,指定三次都一样。异常我接手排查时发现,文件位置上传接口返回的上传HTTP状态码是200,但文件根本没落到指定位置。指定
这不是异常孤例。过去两个月,文件位置我陆续收到六起类似反馈,上传涉及图片、指定PDF和CSV三种格式。共同点是:文件小于5MB时一切正常,超过10MB就大概率失败。更诡异的是,失败时后端日志里连报错都没有——仿佛文件在传输途中被什么东西悄悄吃掉了。
先查Nginx配置。
client_max_body_size设的是50MB,够用。再看PHP的upload_max_filesize和post_max_size,分别是64MB和80MB。表面看没问题。但当我用curl模拟上传一个12MB的文件时,发现请求体被截断了——Content-Length声明的是12MB,实际到达后端的只有不到8MB。中间少掉的4MB去哪了?抓包分析后真相浮出水面。公司内网有一台老旧的WAF设备,它会对所有POST请求做内容检查。当检测到文件包含特定二进制特征(比如某些PDF的字体嵌入数据)时,WAF会主动丢弃后续数据包,但又不返回错误码。于是Nginx以为客户端断开了,PHP的
$_FILES数组里error值为0,但tmp_name指向的文件不完整。后端代码只检查了error === UPLOAD_ERR_OK,没校验文件大小是否与Content-Length一致,直接move_uploaded_file,结果就是0字节或残缺文件。这种问题最难查。因为所有常规检查点都显示“正常”。WAF的日志里只记录“策略匹配:文件内容过滤”,不记录具体丢弃了多少字节。Nginx的
access_log里请求状态是200。PHP的error_log干干净净。你只能靠对比Content-Length和实际落盘大小来发现异常。我后来在代码里加了一段校验:上传完成后,先
filesize($_FILES['file']['tmp_name']),再和$_SERVER['CONTENT_LENGTH']做比较(注意要减去表单其他字段的长度)。如果不一致,直接返回500并记录原始长度和实际长度。这个改动上线后,同类问题再没漏报过。但根本解决得靠运维。那台WAF已经服役七年,厂商早就不维护了。我们把它从上传链路上摘掉,换成了基于ModSecurity的新规则,只检查文件头而不做全量内容扫描。切换当晚,一个15MB的PSD文件上传成功,耗时4.2秒。之前它会在第3秒左右被静默截断。
如果你也遇到上传文件“消失”但日志无异常的情况,先别怀疑代码。拿
顶: 36547踩: 699curl -v看请求体是否完整到达,再对比服务端实际接收的字节数。中间任何一层——负载均衡、WAF、CDN、甚至某些杀毒软件的实时扫描——都可能悄悄吃掉你的数据。文件上传指定位置异常,往往不是“位置”错了,而是文件根本没到齐。






评论专区