javafilepdfboxmessage-body

RESTEASY/JAVAX - How to receive multiples files [RESOLVED]


I have this MessageBody, to read PDF files. When performing a multipart-formdata in Insomnia passing two PDF files, I am only receiving one. The intention is to receive multiple files in the Multipart-formdata, and each file to be an index of the InputStream[] array, so that I can merge them all and thus return a single file. But I can't get more than 1 file (the first one from the request).

import javax.ws.rs.Produces;
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.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.Arrays;


@Provider
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public class MessagePdfBodyReader implements MessageBodyReader<InputStream[]> {

    @Override
    public boolean isReadable( Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType ) {
        return mediaType.isCompatible( MediaType.valueOf( "application/pdf" ) );
    }

    @Override
    public InputStream[] readFrom( Class<InputStream[]> aClass, Type type, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> multivaluedMap, InputStream inputStream ) throws IOException, WebApplicationException {
        ByteArrayOutputStream output = new ByteArrayOutputStream();
        byte[] buffer = new byte[4096];
        int bytesRead;
        while ((bytesRead = inputStream.read(buffer)) != -1) {
            output.write(buffer, 0, bytesRead);
        }
        byte[] pdfBytes = output.toByteArray();

        return new InputStream[] { new ByteArrayInputStream(pdfBytes) };
    }
}

Class to merge PDF's:

public class MergePdfService {

    public byte[] executeService( InputStream[] pdffiles ) throws IOException, DocumentException {

        return mergePdfs( pdffiles );
    }

    private byte[] mergePdfs(InputStream[] pdfFiles) throws IOException {
        PDFMergerUtility merger = new PDFMergerUtility();
        PDDocument merged = new PDDocument();

        for (InputStream pdfFile : pdfFiles) {
            File tempFile = File.createTempFile("temp", ".pdf");
            try (FileOutputStream fos = new FileOutputStream(tempFile)) {
                byte[] buffer = new byte[1024];
                int bytesRead;
                while ((bytesRead = pdfFile.read(buffer)) != -1) {
                    fos.write(buffer, 0, bytesRead);
                }
            }


            try (PDDocument doc = Loader.loadPDF(tempFile)) {
                merger.appendDocument(merged, doc);
            } finally {
                tempFile.delete();
                System.out.println("Arquivo temporário deletado com sucesso!");
            }
        }

        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            merged.save( baos );
            System.out.println(pdfFiles.length);
            return baos.toByteArray();
        }
    }
}

How i serialize: "@MultipartForm MergePdfModel form" I use this import: org.jboss.resteasy.annotations.providers.multipart.MultipartForm;

The MergePdfModel class:


import org.jboss.resteasy.annotations.providers.multipart.PartType;

import javax.ws.rs.FormParam;
import javax.ws.rs.core.MediaType;
import java.io.InputStream;
import java.util.List;

public class MergePdfModel {

    @FormParam("pdfFiles")
    @PartType(MediaType.MULTIPART_FORM_DATA)
    private InputStream[] pdfFiles;

    @FormParam("pdfName")
    @PartType(MediaType.TEXT_PLAIN)
    private String pdfName;



    public void setPdfName( String pdfName ) {
        this.pdfName = pdfName;
    }

    public String getPdfName() {
        return pdfName;
    }


    public InputStream[] getPdfFiles() {
        return pdfFiles;
    }

    public void setPdfFiles( InputStream[] pdfFiles ) {
        this.pdfFiles = pdfFiles;
    }
}

This is my first post here,patience please.

I'm trying to receive multiple PDF files in the request using the MessageBodyReader, merge them all into a single file, and return it.


Solution

  • I managed to solve it, without needing the custom MessageBodyReader, using MultipartFormDataInput, this way it is possible to receive files of various extensions and manipulate them:

    My "Controller":

        public class MergePdfController {
            @POST
            @Path("execute/mergepdf")
            @Consumes(MediaType.MULTIPART_FORM_DATA)
            @Produces(MediaType.APPLICATION_OCTET_STREAM)
            public Response execute(MultipartFormDataInput input) {
                try {
                    Map<String, List<InputPart>> uploadForm = input.getFormDataMap();
                    List<InputPart> inputParts = uploadForm.get("pdfFiles");
                    if (inputParts == null || inputParts.isEmpty()) {
                        return Response.status(Response.Status.BAD_REQUEST)
                                .entity("Nenhum arquivo PDF foi enviado")
                                .build();
                    }
                    List<InputStream> inputStreams = new ArrayList<>();
        
                    for (InputPart inputPart : inputParts) {
                        InputStream inputStream = inputPart.getBody(InputStream.class, null);
                        inputStreams.add(inputStream);
                    }
                    byte[] mergedPdfBytes = new MergePdfService().executeService(inputStreams);
        
        
                    return Response.ok(mergedPdfBytes, MediaType.APPLICATION_OCTET_STREAM_TYPE)
                            .header("Content-Disposition", "attachment; filename=merged.pdf")
                            .build();
    

    my "model" to receive the MultipartFormDataInput:

    import org.jboss.resteasy.annotations.providers.multipart.PartType;
    import org.jboss.resteasy.plugins.providers.multipart.InputPart;
    
    import javax.ws.rs.FormParam;
    import javax.ws.rs.core.MediaType;
    import java.util.List;
    
    public class MergePdfModel {
    
        @FormParam("pdfFiles")
        @PartType(MediaType.APPLICATION_OCTET_STREAM)
        private List<InputPart> pdfFiles;
    
        public void setPdfFiles(List<InputPart> pdfFiles) {
            this.pdfFiles = pdfFiles;
        }
    
        public List<InputPart> getPdfFiles() {
            return pdfFiles;
        }
    }
    

    My class to merge PDF's:

    public class MergePdfService {
    
        public byte[] executeService(List<InputStream> pdffiles) throws IOException, DocumentException {
            System.out.println("EXECUTANDO O MERGE...");
            return mergePdfs(pdffiles);
        }
    
        private byte[] mergePdfs(List<InputStream> pdfFiles) throws IOException {
            PDFMergerUtility merger = new PDFMergerUtility();
            PDDocument merged = new PDDocument();
    
            for (InputStream pdfFile : pdfFiles) {
                File tempFile = File.createTempFile("temp", ".pdf");
                try (FileOutputStream fos = new FileOutputStream(tempFile)) {
                    byte[] buffer = new byte[1024];
                    int bytesRead;
                    while ((bytesRead = pdfFile.read(buffer)) != -1) {
                        fos.write(buffer, 0, bytesRead);
                    }
                }
    
                try (PDDocument doc = Loader.loadPDF(tempFile)) {
                    merger.appendDocument(merged, doc);
                } finally {
                    tempFile.delete();
                }
            }
    
            System.out.println("ARQUIVOS TEMPORÁRIOS CRIADOS E EXCLUIDOS");
    
            try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                merged.save(baos);
                System.out.println("ARQUIVOS MERGEADOS!");
                return baos.toByteArray();
            }
        }
    }
    

    Insomnia test

    Thanks for help :D