Flujos binarios de entrada y salida utilizando JERSEY?

Estoy usando Jersey para implementar una API RESTful que es principalmente recuperar y servir datos codificados JSON. Pero tengo algunas situaciones en las que necesito lograr lo siguiente:

  • Exportar documentos descargables, como PDF, XLS, ZIP u otros archivos binarios.
  • Recupere datos de varias partes, como JSON más un archivo XLS cargado

Tengo un cliente web basado en JQuery de una sola página que crea llamadas AJAX a este servicio web. Por el momento, no hace envíos de formularios, y usa GET y POST (con un objeto JSON). ¿Debo utilizar una publicación de formulario para enviar datos y un archivo binario adjunto, o puedo crear una solicitud de varias partes con el archivo binario JSON plus?

La capa de servicio de mi aplicación crea actualmente un ByteArrayOutputStream cuando genera un archivo PDF. ¿Cuál es la mejor manera de mostrar esta secuencia al cliente a través de Jersey? Creé un MessageBodyWriter, pero no sé cómo usarlo desde un recurso de Jersey. ¿Ese es el enfoque correcto?

He estado revisando las muestras incluidas con Jersey, pero aún no he encontrado nada que ilustre cómo hacer cualquiera de estas cosas. Si es importante, estoy usando Jersey con Jackson para hacer Object-> JSON sin el paso XML y no estoy realmente utilizando JAX-RS.

Pude obtener un archivo ZIP o un archivo PDF extendiendo el objeto StreamingOutput . Aquí hay un código de muestra:

 @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 clase PDFGenerator (mi propia clase para crear el PDF) toma el flujo de salida del método de escritura y lo escribe en lugar de una secuencia de salida recién creada.

No sé si es la mejor manera de hacerlo, pero funciona.

Tuve que devolver un archivo rtf y esto funcionó para mí.

 // 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(); 

Estoy usando este código para exportar el archivo excel (xlsx) (Apache Poi) en jersey como un adjunto.

 @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(); } 

Aquí hay otro ejemplo. Estoy creando un QRCode como PNG a través de ByteArrayOutputStream . El recurso devuelve un objeto Response , y los datos de la secuencia son la entidad.

Para ilustrar el manejo del código de respuesta, he agregado el manejo de los encabezados del caché ( 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; } 

Por favor, no me stream.toByteArray() una paliza en caso de que stream.toByteArray() sea ​​un no-no sabio en memoria 🙂 Funciona para mis archivos <1KB PNG ...

He estado componiendo mis servicios de Jersey 1.17 de la siguiente manera:

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); } 

Y el cliente, si lo necesita:

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(); } 

Este ejemplo muestra cómo publicar archivos de registro en JBoss a través de un recurso de reposo. Tenga en cuenta que el método get usa la interfaz StreamingOutput para transmitir el contenido del archivo de registro.

 @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(); } } 

}

Usar Jersey 2.16 La descarga de archivos es muy fácil.

A continuación se muestra el ejemplo del archivo 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(); } 

Me pareció útil lo siguiente y quise compartirlo en caso de que lo ayude a usted o a alguien más. Quería algo como MediaType.PDF_TYPE, que no existe, pero este código hace lo mismo:

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

Ver http://jersey.java.net/nonav/apidocs/1.1.0-ea/contribs/jersey-multipart/com/sun/jersey/multipart/file/DefaultMediaTypePredictor.CommonMediaTypes.html

En mi caso, estaba publicando un documento PDF en otro sitio:

 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"))); 

Entonces p se pasa como el segundo parámetro para publicar ().

Este enlace fue útil para mí al juntar este fragmento de código: http://jersey.576304.n2.nabble.com/Multipart-Post-td4252846.html

Esto funcionó bien conmigo 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(); } 

Otro código de muestra donde puede cargar un archivo al servicio REST, el servicio REST cierra el archivo y el cliente descarga el archivo zip del servidor. Este es un buen ejemplo del uso de flujos de entrada y salida binarios usando Jersey.

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

Esta respuesta fue publicada por mí en otro hilo. Espero que esto ayude.