DEBUG.WIKI
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS
IPPeak:从目标地区访问网页,住宅代理、SEO 监测、广告验证
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国

Micronaut 上传文件的 mediaType 为 null,检查 multipart 头部大小写

Micronaut 受影响的 multipart 解析路径会因 part 头名大小写丢失媒体类型。Core 5.2.15 使用忽略大小写比较,验收应同时检查两种头名和文件内容。

上传接口返回 200,文件内容也能读到,但 StreamingFileUpload.metadata().mediaType() 为 null,先看 multipart 文件 part 内部的 Content-Type。请求最外层的 Content-Type: multipart/form-data; boundary=... 只说明整个请求的格式,不能代替文件 part 的媒体类型。

受影响的 Micronaut 5 上传解析路径会出现这样的反差:

Content-Disposition: form-data; name="file"; filename="test.webp"
Content-Type: image/webp

这份 part 的媒体类型丢失;把同一行的头名改成 content-type,值仍是 image/webp,元数据却能读到。标准大小写的头名本身没有错误。此问题也有 CompletedFileUpload.getContentType() 返回空 Optional 的对应表现。

FormDemuxer 用 Netty 的小写常量 content-type 去匹配解码器交来的原始头名,旧比较调用的是区分大小写的 contentEquals。正文照常传递,媒体类型却没有写进元数据,所以“文件收到”与“类型读不到”可以同时发生。这里丢的是头名匹配结果,修改扩展名或把 image/webp 改成大写无法修复。

Micronaut Core 5.2.15 的 FormDemuxer 已对 Content-Type、Content-Length 使用 contentEqualsIgnoreCase。由现有平台或 Core BOM 将 micronaut-http-server-netty 及相关 Core 组件统一到包含这项处理的版本,再重建应用。不能只因为平台版本号相近,就假定实际 server-netty jar 已更新;可先核依赖解析结果:

./gradlew dependencyInsight   --dependency micronaut-http-server-netty   --configuration runtimeClasspath

对于 Maven 项目,可用 mvn dependency:tree -Dincludes=io.micronaut:micronaut-http-server-netty 查看实际版本,再在原有 dependencyManagement 中调整。

验收应保留两个仅头名大小写不同的请求。准备一个测试文件,向自己的上传端点发送:

curl -F 'file=@test.webp;type=image/webp' http://localhost:8080/upload

在端点或集成测试中断言元数据媒体类型为 image/webp,再用原始 multipart 请求发送小写 content-type 的对照,确认两者都得到相同类型和相同文件内容。curl 退出成功或接口仍返回 200,都不足以说明元数据已恢复。

如果短期只能控制测试客户端,把 part 头名写成小写可以隔离这个故障,但不宜把它变成客户端接入要求。服务端仍应接收合法的不同大小写。若请求根本没给该 part 设置 Content-Type,或上传已经在大小限制、boundary、流消费阶段失败,则需要另查;这项修复不会凭空推断文件内容的 MIME 类型。