Fluxos binários de input e saída usando JERSEY?

Estou usando o Jersey para implementar uma API RESTful que recupera e serve principalmente dados codificados em JSON. Mas tenho algumas situações em que preciso realizar o seguinte:

  • Exporte documentos para download, como PDF, XLS, ZIP ou outros arquivos binários.
  • Recuperar dados multipartes, como JSON e um arquivo XLS carregado

Eu tenho um cliente Web baseado em JQuery de página única que cria chamadas AJAX para este serviço da web. No momento, ele não envia formulários e usa GET e POST (com um object JSON). Devo utilizar uma postagem de formulário para enviar dados e um arquivo binário anexado ou posso criar uma solicitação multipartes com o arquivo binário JSON plus?

Atualmente, a camada de serviço do meu aplicativo cria um ByteArrayOutputStream quando gera um arquivo PDF. Qual é a melhor maneira de gerar esse stream para o cliente via Jersey? Eu criei um MessageBodyWriter, mas eu não sei como usá-lo de um recurso de Jersey. Essa é a abordagem correta?

Eu tenho procurado através das amostras incluídas com Jersey, mas ainda não encontrei nada que ilustre como fazer qualquer uma dessas coisas. Se for importante, estou usando o Jersey com o Jackson para fazer o Object-> JSON sem o passo XML e não estou realmente utilizando o JAX-RS.

Eu consegui obter um arquivo ZIP ou um arquivo PDF, estendendo o object StreamingOutput . Aqui está um código de amostra:

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

A class PDFGenerator (minha própria class para criar o PDF) pega o stream de saída do método write e grava nele em vez de um stream de saída recém-criado.

Não sei se é a melhor maneira de fazer isso, mas funciona.

Eu tive que retornar um arquivo RTF e isso funcionou para mim.

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

Estou usando este código para exportar o arquivo excel (xlsx) (Apache Poi) em jersey como um anexo.

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

Aqui está outro exemplo. Estou criando um QRCode como PNG por meio de um ByteArrayOutputStream . O recurso retorna um object Response e os dados do stream são a entidade.

Para ilustrar o tratamento do código de resposta, adicionei a manipulação de headers 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; } 

Por favor, não me bata no caso stream.toByteArray() é um não-não memory sábio 🙂 Ele funciona para meus arquivos PNG <1 KB ...

Tenho vindo a compor os meus serviços de Jersey 1.17 da seguinte forma:

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

E o cliente, se você precisar dele:

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 exemplo mostra como publicar arquivos de log no JBoss por meio de um recurso de descanso. Observe que o método get usa a interface StreamingOutput para transmitir o conteúdo do arquivo de log.

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

}

Usando Jersey 2.16 Download de arquivo é muito fácil.

Abaixo está o exemplo para o arquivo 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(); } 

Eu achei o seguinte útil para mim e eu queria compartilhar, caso isso ajude você ou outra pessoa. Eu queria algo como MediaType.PDF_TYPE, que não existe, mas esse código faz a mesma coisa:

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

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

No meu caso eu estava postando um documento em PDF para outro 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"))); 

Então p é passado como o segundo parâmetro para postar ().

Este link foi útil para mim ao colocar este trecho de código juntos: http://jersey.576304.n2.nabble.com/Multipart-Post-td4252846.html

Isso funcionou bem comigo: 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(); } 

Outro código de amostra no qual você pode fazer upload de um arquivo para o serviço REST, o serviço REST fecha o arquivo e o cliente faz o download do arquivo zip do servidor. Este é um bom exemplo do uso de streams binários de input e saída usando Jersey.

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

Esta resposta foi postada por mim em outro tópico. Espero que isto ajude.