You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.17 08:17:59