web-dev-qa-db-fra.com

Flux binaires d'entrée et de sortie utilisant JERSEY?

J'utilise Jersey pour implémenter une API RESTful qui consiste principalement à extraire et à servir des données codées JSON. Mais il y a des situations dans lesquelles je dois accomplir les tâches suivantes:

  • Exportez des documents téléchargeables, tels que PDF, XLS, Zip ou autres fichiers binaires.
  • Récupérer des données en plusieurs parties, telles que certains JSON et un fichier XLS téléchargé

J'ai un client Web d'une seule page basé sur JQuery qui crée des appels AJAX) à ce service Web. Pour le moment, il ne soumet pas le formulaire et utilise GET et POST (avec un objet JSON) Dois-je utiliser une publication de formulaire pour envoyer des données et un fichier binaire attaché, ou puis-je créer une demande multipartie avec JSON plus un fichier binaire?

La couche de service de mon application crée actuellement un ByteArrayOutputStream quand elle génère un fichier PDF. Quel est le meilleur moyen de générer ce flux sur le client via Jersey? J'ai créé un MessageBodyWriter, mais je ne t savoir comment l’utiliser à partir d’une ressource de Jersey, est-ce la bonne approche?

J'ai parcouru les exemples fournis avec Jersey, mais je n'ai encore rien trouvé qui montre comment faire l'une ou l'autre de ces choses. Si cela compte, j'utilise Jersey avec Jackson pour réaliser Object-> JSON sans l'étape XML et je n'utilise pas vraiment JAX-RS.

109
Tauren

J'ai réussi à obtenir un fichier Zip ou un fichier PDF en développant l'objet StreamingOutput. Voici un exemple de code:

@Path("PDF-file.pdf/")
@GET
@Produces({"application/pdf"})
public StreamingOutput getPDF() throws Exception {
    return new StreamingOutput() {
        public void write(OutputStream output) throws IOException, WebApplicationException {
            try {
                PDFGenerator generator = new PDFGenerator(getEntity());
                generator.generatePDF(output);
            } catch (Exception e) {
                throw new WebApplicationException(e);
            }
        }
    };
}

La classe PDFGenerator (ma propre classe pour la création du fichier PDF) prend le flux de sortie de la méthode write et écrit dans celle-ci au lieu d'un flux de sortie nouvellement créé.

Je ne sais pas si c'est la meilleure façon de le faire, mais ça fonctionne.

108
MikeTheReader

Je devais retourner un fichier RTF et cela a fonctionné pour moi.

// create a byte array of the file in correct format
byte[] docStream = createDoc(fragments); 

return Response
            .ok(docStream, MediaType.APPLICATION_OCTET_STREAM)
            .header("content-disposition","attachment; filename = doc.rtf")
            .build();
29
Abhishek Rakshit

J'utilise ce code pour exporter un fichier Excel (xlsx) (Apache Poi) en jersey en tant que pièce jointe.

@GET
@Path("/{id}/contributions/Excel")
@Produces("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
public Response exportExcel(@PathParam("id") Long id)  throws Exception  {

    Resource resource = new ClassPathResource("/xls/template.xlsx");

    final InputStream inp = resource.getInputStream();
    final Workbook wb = WorkbookFactory.create(inp);
    Sheet sheet = wb.getSheetAt(0);

    Row row = CellUtil.getRow(7, sheet);
    Cell cell = CellUtil.getCell(row, 0);
    cell.setCellValue("TITRE TEST");

    [...]

    StreamingOutput stream = new StreamingOutput() {
        public void write(OutputStream output) throws IOException, WebApplicationException {
            try {
                wb.write(output);
            } catch (Exception e) {
                throw new WebApplicationException(e);
            }
        }
    };


    return Response.ok(stream).header("content-disposition","attachment; filename = export.xlsx").build();

}
22
Grégory

Voici un autre exemple. Je crée un QRCode en tant que PNG via un ByteArrayOutputStream. La ressource retourne un objet Response et les données du flux sont l'entité.

Pour illustrer la gestion du code de réponse, j'ai ajouté la gestion des en-têtes de cache (If-modified-since, If-none-matches, Etc.).

@Path("{externalId}.png")
@GET
@Produces({"image/png"})
public Response getAsImage(@PathParam("externalId") String externalId, 
        @Context Request request) throws WebApplicationException {

    ByteArrayOutputStream stream = new ByteArrayOutputStream();
    // do something with externalId, maybe retrieve an object from the
    // db, then calculate data, size, expirationTimestamp, etc

    try {
        // create a QRCode as PNG from data     
        BitMatrix bitMatrix = new QRCodeWriter().encode(
                data, 
                BarcodeFormat.QR_CODE, 
                size, 
                size
        );
        MatrixToImageWriter.writeToStream(bitMatrix, "png", stream);

    } catch (Exception e) {
        // ExceptionMapper will return HTTP 500 
        throw new WebApplicationException("Something went wrong …")
    }

    CacheControl cc = new CacheControl();
    cc.setNoTransform(true);
    cc.setMustRevalidate(false);
    cc.setNoCache(false);
    cc.setMaxAge(3600);

    EntityTag etag = new EntityTag(HelperBean.md5(data));

    Response.ResponseBuilder responseBuilder = request.evaluatePreconditions(
            updateTimestamp,
            etag
    );
    if (responseBuilder != null) {
        // Preconditions are not met, returning HTTP 304 'not-modified'
        return responseBuilder
                .cacheControl(cc)
                .build();
    }

    Response response = Response
            .ok()
            .cacheControl(cc)
            .tag(etag)
            .lastModified(updateTimestamp)
            .expires(expirationTimestamp)
            .type("image/png")
            .entity(stream.toByteArray())
            .build();
    return response;
}   

S'il vous plaît ne me bat pas dans le cas où stream.toByteArray() est une mémoire non-pas sage :) Cela fonctionne pour mes fichiers PNG <1 Ko ...

15
Hank

J'ai composé mes services Jersey 1.17 de la manière suivante:

FileStreamingOutput

public class FileStreamingOutput implements StreamingOutput {

    private File file;

    public FileStreamingOutput(File file) {
        this.file = file;
    }

    @Override
    public void write(OutputStream output)
            throws IOException, WebApplicationException {
        FileInputStream input = new FileInputStream(file);
        try {
            int bytes;
            while ((bytes = input.read()) != -1) {
                output.write(bytes);
            }
        } catch (Exception e) {
            throw new WebApplicationException(e);
        } finally {
            if (output != null) output.close();
            if (input != null) input.close();
        }
    }

}

GET

@GET
@Produces("application/pdf")
public StreamingOutput getPdf(@QueryParam(value="name") String pdfFileName) {
    if (pdfFileName == null)
        throw new WebApplicationException(Response.Status.BAD_REQUEST);
    if (!pdfFileName.endsWith(".pdf")) pdfFileName = pdfFileName + ".pdf";

    File pdf = new File(Settings.basePath, pdfFileName);
    if (!pdf.exists())
        throw new WebApplicationException(Response.Status.NOT_FOUND);

    return new FileStreamingOutput(pdf);
}

Et le client, si vous en avez besoin:

Client

private WebResource resource;

public InputStream getPDFStream(String filename) throws IOException {
    ClientResponse response = resource.path("pdf").queryParam("name", filename)
        .type("application/pdf").get(ClientResponse.class);
    return response.getEntityInputStream();
}
14
Daniel Szalay

Cet exemple montre comment publier des fichiers journaux dans JBoss via une ressource restante. Notez que la méthode get utilise l'interface StreamingOutput pour diffuser le contenu du fichier journal.

@Path("/logs/")
@RequestScoped
public class LogResource {

private static final Logger logger = Logger.getLogger(LogResource.class.getName());
@Context
private UriInfo uriInfo;
private static final String LOG_PATH = "jboss.server.log.dir";

public void pipe(InputStream is, OutputStream os) throws IOException {
    int n;
    byte[] buffer = new byte[1024];
    while ((n = is.read(buffer)) > -1) {
        os.write(buffer, 0, n);   // Don't allow any extra bytes to creep in, final write
    }
    os.close();
}

@GET
@Path("{logFile}")
@Produces("text/plain")
public Response getLogFile(@PathParam("logFile") String logFile) throws URISyntaxException {
    String logDirPath = System.getProperty(LOG_PATH);
    try {
        File f = new File(logDirPath + "/" + logFile);
        final FileInputStream fStream = new FileInputStream(f);
        StreamingOutput stream = new StreamingOutput() {
            @Override
            public void write(OutputStream output) throws IOException, WebApplicationException {
                try {
                    pipe(fStream, output);
                } catch (Exception e) {
                    throw new WebApplicationException(e);
                }
            }
        };
        return Response.ok(stream).build();
    } catch (Exception e) {
        return Response.status(Response.Status.CONFLICT).build();
    }
}

@POST
@Path("{logFile}")
public Response flushLogFile(@PathParam("logFile") String logFile) throws URISyntaxException {
    String logDirPath = System.getProperty(LOG_PATH);
    try {
        File file = new File(logDirPath + "/" + logFile);
        PrintWriter writer = new PrintWriter(file);
        writer.print("");
        writer.close();
        return Response.ok().build();
    } catch (Exception e) {
        return Response.status(Response.Status.CONFLICT).build();
    }
}    

}

7
Jaime Casero

Utiliser Jersey 2.16 Le téléchargement de fichier est très facile.

Voici l'exemple du fichier Zip

@GET
@Path("zipFile")
@Produces("application/Zip")
public Response getFile() {
    File f = new File(Zip_FILE_PATH);

    if (!f.exists()) {
        throw new WebApplicationException(404);
    }

    return Response.ok(f)
            .header("Content-Disposition",
                    "attachment; filename=server.Zip").build();
}
6
orangegiraffa

J'ai trouvé ce qui suit utile pour moi et je voulais le partager au cas où cela vous aiderait ou aiderait quelqu'un d'autre. Je voulais quelque chose comme MediaType.PDF_TYPE, qui n'existe pas, mais ce code fait la même chose:

DefaultMediaTypePredictor.CommonMediaTypes.
        getMediaTypeFromFileName("anything.pdf")

Voir http://jersey.Java.net/nonav/apidocs/1.1.0-ea/contribs/jersey-multipart/com/Sun/jersey/multipart/file/DefaultMediaTypePredictor.CommonMediaTypes.html

Dans mon cas, je publiais un PDF document sur un autre site:

FormDataMultiPart p = new FormDataMultiPart();
p.bodyPart(new FormDataBodyPart(FormDataContentDisposition
        .name("fieldKey").fileName("document.pdf").build(),
        new File("path/to/document.pdf"),
        DefaultMediaTypePredictor.CommonMediaTypes
                .getMediaTypeFromFileName("document.pdf")));

Ensuite, p est passé en tant que second paramètre de post ().

Ce lien m'a été utile pour assembler cet extrait de code: http://jersey.576304.n2.nabble.com/Multipart-Post-td4252846.html

5
Dovev Hefetz

Cela a bien fonctionné avec moi url: http://example.com/rest/muqsith/get-file?filePath=C :\Users\I066807\Desktop\test.xml

@GET
@Produces({ MediaType.APPLICATION_OCTET_STREAM })
@Path("/get-file")
public Response getFile(@Context HttpServletRequest request){
   String filePath = request.getParameter("filePath");
   if(filePath != null && !"".equals(filePath)){
        File file = new File(filePath);
        StreamingOutput stream = null;
        try {
        final InputStream in = new FileInputStream(file);
        stream = new StreamingOutput() {
            public void write(OutputStream out) throws IOException, WebApplicationException {
                try {
                    int read = 0;
                        byte[] bytes = new byte[1024];

                        while ((read = in.read(bytes)) != -1) {
                            out.write(bytes, 0, read);
                        }
                } catch (Exception e) {
                    throw new WebApplicationException(e);
                }
            }
        };
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    }
        return Response.ok(stream).header("content-disposition","attachment; filename = "+file.getName()).build();
        }
    return Response.ok("file path null").build();
}
4
Muqsith

Un autre exemple de code dans lequel vous pouvez télécharger un fichier vers le service REST, le service REST) compresse le fichier et le client télécharge le fichier Zip à partir du serveur. C'est un bon exemple d'utilisation de flux d'entrée et de sortie binaires à l'aide de Jersey.

https://stackoverflow.com/a/32253028/15789

Cette réponse a été postée par moi dans un autre fil. J'espère que cela t'aides.

1
RuntimeException