JBoss项目中Resteasy解析.eml文件触发NPE问题求助
问题描述
在Java JBoss项目中使用Resteasy API实现文件上传功能,普通文件上传正常,但上传.eml文件时触发NullPointerException(NPE)。该问题对应RESTEASY-1255工单,当前使用Resteasy版本为3.0.24.final。
已尝试的解决方案
查阅关联工单RESTEASY-754得知该问题在4.0.0.final版本中已修复,于是将resteasy-multipart-provider升级至4.0.0.CR1:
<dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-multipart-provider</artifactId> <version>4.0.0.CR1</version> <scope>provided</scope> </dependency>
但问题仍未解决。
代码片段
Controller接口
import org.jboss.resteasy.annotations.providers.multipart.MultipartForm; import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput; @POST @GZIP @Produces(MediaType.TEXT_PLAIN) @Consumes(MediaType.MULTIPART_FORM_DATA) @Path("/upload") public Response uploadSomeDocument(@MultipartForm MultipartFormDataInput input);
Controller实现类
import org.jboss.resteasy.plugins.providers.multipart.InputPart; import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput; @Override public Response uploadSomeDocument(MultipartFormDataInput input) { Map<String, List<InputPart>> formDataMap = input.getFormDataMap(); List<InputPart> inputParts = formDataMap.get("file"); // inputStream为null InputStream inputStream = inputPart.getBody(InputStream.class, null); // 此处触发NPE byte[] bytes = IOUtils.toByteArray(inputStream); // 后续字节操作 }
pom.xml
<dependencyManagement> <dependencies> <dependency> <groupId>org.jboss.bom</groupId> <artifactId>jboss-eap-javaee7-with-tools</artifactId> <version>7.1.0.GA</version> <type>pom</type> <scope>import</scope> </dependency> <dependency> <groupId>org.jboss.spec</groupId> <artifactId>jboss-javaee-7.0</artifactId> <version>1.1.0.Final-redhat-3</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.jboss.spec.javax.servlet</groupId> <artifactId>jboss-servlet-api_3.1_spec</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.jboss.spec.javax.ws.rs</groupId> <artifactId>jboss-jaxrs-api_2.0_spec</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.jboss.spec.javax.annotation</groupId> <artifactId>jboss-annotations-api_1.2_spec</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-jaxrs</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.jboss.logging</groupId> <artifactId>jboss-logging</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-multipart-provider</artifactId> <scope>provided</scope> </dependency> </dependencies>
堆栈信息
Caused by: java.lang.NullPointerException at org.apache.commons.io.IOUtils.copyLarge(IOUtils.java:2314) [commons-io-2.6.jar:2.6] at org.apache.commons.io.IOUtils.copy(IOUtils.java:2270) [commons-io-2.6.jar:2.6] at org.apache.commons.io.IOUtils.copyLarge(IOUtils.java:2291) [commons-io-2.6.jar:2.6] at org.apache.commons.io.IOUtils.copy(IOUtils.java:2246) [commons-io-2.6.jar:2.6] at org.apache.commons.io.IOUtils.toByteArray(IOUtils.java:765) [commons-io-2.6.jar:2.6] at com.xxx.repository.SOUploadFileRepository.write(SOUploadFileRepository.java:64) [data-scx-2.0.188-SNAPSHOT.jar:] at com.xxx.so.SalesOrderServiceImpl.uploadCustomerPoDocument(SalesOrderServiceImpl.java:2179) [data-scx-2.0.188-SNAPSHOT.jar:] at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [rt.jar:1.8.0_362] at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) [rt.jar:1.8.0_362] at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) [rt.jar:1.8.0_362] at java.lang.reflect.Method.invoke(Method.java:498) [rt.jar:1.8.0_362] at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:140) [resteasy-jaxrs-3.0.24.Final-redhat-1.jar:3.0.24.Final-redhat-1] at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:295) [resteasy-jaxrs-3.0.24.Final-redhat-1.jar:3.0.24.Final-redhat-1] at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:249) [resteasy-jaxrs-3.0.24.Final-redhat-1.jar:3.0.24.Final-redhat-1] at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:236) [resteasy-jaxrs-3.0.24.Final-redhat-1.jar:3.0.24.Final-redhat-1] at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:406) [resteasy-jaxrs-3.0.24.Final-redhat-1.jar:3.0.24.Final-redhat-1] ... 43 more
运行环境
项目打包为war文件,部署在JBoss EAP 7.1环境中。
解决方案方向建议
统一Resteasy版本
从堆栈信息可见,resteasy-jaxrs仍在使用3.0.24版本,仅升级multipart-provider会导致版本不兼容。需将所有Resteasy相关依赖(如resteasy-jaxrs、resteasy-multipart-provider)统一升级到4.0.0.Final或更高稳定版本;若容器自带版本冲突,需将依赖scope改为compile,并通过jboss-deployment-structure.xml排除容器自带模块。强制二进制解析.eml文件
.eml文件的Content-Type为message/rfc822,旧版本Resteasy对该类型解析存在bug。可手动设置InputPart的媒体类型为二进制,强制按字节流解析:for (InputPart inputPart : inputParts) { inputPart.setMediaType(MediaType.APPLICATION_OCTET_STREAM_TYPE); InputStream inputStream = inputPart.getBody(InputStream.class, null); if (inputStream == null) { return Response.status(Response.Status.BAD_REQUEST).entity("文件解析失败").build(); } byte[] bytes = IOUtils.toByteArray(inputStream); // 后续操作 }隔离容器自带依赖
JBoss EAP 7.1自带Resteasy 3.x,会优先加载容器库。在war的WEB-INF目录下创建jboss-deployment-structure.xml,排除容器自带的multipart-provider模块,让应用使用自己打包的版本:<jboss-deployment-structure> <deployment> <exclusions> <module name="org.jboss.resteasy.resteasy-multipart-provider"/> </exclusions> </deployment> </jboss-deployment-structure>升级到官方修复版本
当前使用的4.0.0.CR1是候选版本,建议升级到官方标注修复的4.0.0.Final版本,避免候选版本的遗留问题。增强代码健壮性
在获取inputStream后添加空判断,提前捕获异常,避免NPE扩散:if (inputStream == null) { return Response.status(Response.Status.BAD_REQUEST).entity("文件内容为空或解析失败").build(); }
内容的提问来源于stack exchange,提问作者Michael

