RESTEASY/JAVAX:如何正确接收multipart-formdata中的多个PDF文件?
RESTEASY/JAVAX:如何正确接收multipart-formdata中的多个PDF文件?
兄弟,我看了你遇到的问题——用Insomnia传两个PDF文件到你的JAX-RS接口,结果只收到第一个,想把多文件存到InputStream[]里合并却搞不定。看了你的代码片段,发现几个关键问题,给你两种靠谱的解决方案:
方案一:修复你自定义的MessageBodyReader
首先你的代码里有个致命注解错误:MessageBodyReader是用来读取请求体的,你却加了@Produces(这是标记响应媒体类型的),必须换成@Consumes(MediaType.MULTIPART_FORM_DATA)才行。另外你没完整实现readFrom方法,这才是处理多文件的核心。
下面是修正后的完整代码:
import org.jboss.resteasy.plugins.providers.multipart.InputPart; import org.jboss.resteasy.plugins.providers.multipart.MultipartInput; import org.jboss.resteasy.plugins.providers.multipart.MultipartInputImpl; import javax.ws.rs.Consumes; import javax.ws.rs.WebApplicationException; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.ext.MessageBodyReader; import javax.ws.rs.ext.Provider; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.lang.annotation.Annotation; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.List; @Provider @Consumes(MediaType.MULTIPART_FORM_DATA) // 关键:换成Consumes,指定处理multipart请求 public class MessagePdfBodyReader implements MessageBodyReader<InputStream[]> { @Override public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) { // 只处理InputStream[]类型的参数 return InputStream[].class.equals(type); } @Override public InputStream[] readFrom(Class<InputStream[]> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { // 用Resteasy内置的MultipartInput解析多部分数据,避免手动处理boundary的麻烦 MultipartInput multipartInput = MultipartInputImpl.create(entityStream, mediaType, httpHeaders, null); List<InputStream> pdfStreams = new ArrayList<>(); try { // 遍历每个文件part,转成InputStream存入列表 for (InputPart part : multipartInput.getParts()) { byte[] pdfBytes = part.getBody(byte[].class, null); pdfStreams.add(new ByteArrayInputStream(pdfBytes)); } } finally { // 一定要关闭资源,防止内存泄漏 multipartInput.close(); } // 把列表转成数组返回 return pdfStreams.toArray(new InputStream[0]); } }
注意:要确保你的项目里引入了Resteasy的multipart依赖,比如Maven的话要加:
<dependency> <groupId>org.jboss.resteasy</groupId> <artifactId>resteasy-multipart-provider</artifactId> <version>你的Resteasy版本</version> </dependency>
方案二:直接用现成的Multipart API(更简单)
其实不用自己写MessageBodyReader,JAX-RS(Resteasy)本身就支持多文件上传的处理,直接在接口方法里接收List<InputPart>就行,代码更简洁:
import org.jboss.resteasy.plugins.providers.multipart.InputPart; import javax.ws.rs.Consumes; import javax.ws.rs.POST; import javax.ws.rs.Path; import javax.ws.rs.Produces; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; @Path("/pdf") public class PdfMergeResource { @POST @Path("/merge") @Consumes(MediaType.MULTIPART_FORM_DATA) @Produces(MediaType.APPLICATION_OCTET_STREAM) public Response mergePdfs(@FormParam("files") List<InputPart> fileParts) throws IOException { List<InputStream> pdfStreams = new ArrayList<>(); // 遍历所有上传的文件part for (InputPart part : fileParts) { byte[] pdfBytes = part.getBody(byte[].class, null); pdfStreams.add(new ByteArrayInputStream(pdfBytes)); } // 这里调用你的PDF合并逻辑(比如用Apache PDFBox或iText) InputStream mergedPdf = mergePdfFiles(pdfStreams); // 返回合并后的文件 return Response.ok(mergedPdf) .header("Content-Disposition", "attachment; filename=merged.pdf") .build(); } // 示例合并方法(需要引入PDF处理库) private InputStream mergePdfFiles(List<InputStream> pdfStreams) { // 这里替换成你实际的合并逻辑,比如用PDFBox: /* try (PDDocument mergedDoc = new PDDocument()) { for (InputStream stream : pdfStreams) { try (PDDocument doc = PDDocument.load(stream)) { for (PDPage page : doc.getPages()) { mergedDoc.addPage(page); } } } ByteArrayOutputStream baos = new ByteArrayOutputStream(); mergedDoc.save(baos); return new ByteArrayInputStream(baos.toByteArray()); } catch (IOException e) { throw new WebApplicationException("合并PDF失败", e); } */ return null; // 替换成实际实现 } }
关键注意点:
- 在Insomnia里上传文件时,所有文件的表单字段名要统一设为
files(和@FormParam("files")对应),这样才能被收集到同一个List里 - 合并PDF必须用专门的库(比如Apache PDFBox、iText),绝对不能直接拼接字节流,那样会生成损坏的PDF文件
备注:内容来源于stack exchange,提问作者Italo Almeida
相关产品推荐
相关产品推荐

