XML文件上传必须用@Consumes(MediaType.MULTIPART_FORM_DATA),因HTTP请求体为multipart/form-data而非纯XML;需引入jersey-media-multipart依赖,用@FormDataParam获取InputStream并以JAXBContext解析,同时禁用外部DTD防XXE。

XML文件上传必须用 @Consumes(MediaType.MULTIPART_FORM_DATA)
Jersey 默认不支持 multipart 解析,直接写 @Consumes("application/xml") 会 415 Unsupported Media Type。XML 文件作为二进制附件上传时,HTTP 请求体是 multipart/form-data,不是纯 XML 文本。所以服务端接口必须显式声明接收 multipart,并手动提取其中的 XML 部分。
- 不能只靠
@XmlRootElement类自动绑定 —— 这只适用于纯 XML POST(Content-Type: application/xml),不适用于带文件字段的表单上传 - 必须引入
jersey-media-multipart依赖(Jersey 2.x)或mimepull(旧版),否则@FormDataParam注解无效 - 客户端必须用
multipart/form-data编码,且 XML 文件需作为独立 part 上传,不能拼在 JSON 或文本字段里
用 @FormDataParam 获取 XML 文件流并解析
拿到 InputStream 后,推荐用 JAXBContext 解析(前提是 XML 结构已定义对应 Java 类),避免手动 DOM/SAX 处理。注意:JAXB 默认不处理外部 DTD,若 XML 含 DOCTYPE 且未禁用,会抛 javax.xml.bind.UnmarshalException。
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Unmarshaller;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import org.glassfish.jersey.media.multipart.FormDataParam;
@Path("/upload")
public class XmlUploadResource {
@POST
@Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadXml(
@FormDataParam("file") InputStream xmlStream,
@FormDataParam("file") FormDataContentDisposition fileDetail) {
try {
JAXBContext ctx = JAXBContext.newInstance(MyXmlRoot.class);
Unmarshaller unmarshaller = ctx.createUnmarshaller();
// 关键:禁用 DTD 和外部实体,防 XXE
unmarshaller.setProperty("javax.xml.XMLConstants.ACCESS_EXTERNAL_DTD", "");
unmarshaller.setProperty("javax.xml.XMLConstants.ACCESS_EXTERNAL_SCHEMA", "");
MyXmlRoot obj = (MyXmlRoot) unmarshaller.unmarshal(xmlStream);
return Response.ok("Parsed: " + obj.getId()).build();
} catch (Exception e) {
return Response.status(400).entity("Parse failed: " + e.getMessage()).build();
}
}
}
客户端 curl 示例必须带 -F 和正确字段名
字段名(如 file)必须和服务端 @FormDataParam("file") 一致;XML 文件路径要真实存在,且 curl 会自动设置 Content-Type 和 boundary。
curl -X POST \ http://localhost:8080/api/upload \ -F "file=@/path/to/data.xml"
- 如果服务端用
@FormDataParam("xmlFile"),这里必须写-F "xmlFile=@..." - 不要加
-H "Content-Type: multipart/form-data"—— curl 会自动设置,手动加反而可能破坏 boundary - 若 XML 文件含中文,确保文件本身是 UTF-8 编码,且
声明存在(JAXB 依赖它识别编码)
常见 400 错误:Unable to process form content
这通常不是业务逻辑问题,而是 Jersey multipart 模块没加载成功。检查以下三点:
-
web.xml中是否注册了MultipartConfigFeature(Servlet 3.0+)或MultiPartFeature(Jersey 2.x) - Maven 依赖是否完整:
jersey-media-multipart版本必须和 jersey-server 一致(如 2.35) - Spring Boot 用户注意:不能只靠
spring-boot-starter-jersey,必须额外添加jersey-media-multipart依赖
XXE 防护容易被忽略 —— 即使你的 XML 很简单,一旦服务暴露公网,攻击者可构造恶意 DOCTYPE 触发外联请求或读取本地文件。JAXB 的 ACCESS_EXTERNAL_* 属性必须显式设为空字符串,设为 null 无效。










