Italo Almeida
Italo Almeida

Reputation: 1

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.

Upvotes: 0

Views: 166

Answers (2)

Italo Almeida
Italo Almeida

Reputation: 1

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

Upvotes: 0

g00se
g00se

Reputation: 4296

I don't know this API but I found this works for me. try-with-resources might be be used even more, but I don't know the API well enough. Kudos to anyone who can tighten that up.

package com.technojeeves.pdfboxmerge;

import org.apache.pdfbox.pdmodel.PDDocument;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayOutputStream;
import org.apache.pdfbox.multipdf.PDFMergerUtility;
import java.nio.file.Files;
import java.nio.file.Path;
import org.apache.pdfbox.io.RandomAccessReadBuffer;

public class MergePdfService {

    private byte[] mergePdfs(InputStream[] pdfFiles) throws IOException {
        byte[] result = null;
        PDFMergerUtility merger = new PDFMergerUtility();
        Path tempFile = Files.createTempFile(Path.of(System.getProperty("java.io.tmpdir")), "temp", ".pdf");

        for (InputStream pdfFile : pdfFiles) {
             merger.addSource(new RandomAccessReadBuffer(pdfFile));
        }
        try (OutputStream out = Files.newOutputStream(tempFile)) {
            merger.setDestinationStream(out);
            merger.mergeDocuments(null);
        }

        try (ByteArrayOutputStream baos = new ByteArrayOutputStream();
                InputStream in = Files.newInputStream(tempFile)) {
            in.transferTo(baos);
            result = baos.toByteArray();
        }
        return result;
    }

    public static void main(String[] args) throws Exception {    
        try (InputStream a = Files.newInputStream(Path.of("A.pdf"));
        InputStream b = Files.newInputStream(Path.of("B.pdf"));
        InputStream c = Files.newInputStream(Path.of("C.pdf"))) {
            InputStream[] streams = new InputStream[] { a, b, c };
            System.out.write(new MergePdfService().mergePdfs(streams));
        }
    }

}

Upvotes: 0

Related Questions