Ingresso e uscita di stream binari usando JERSEY?

Sto utilizzando Jersey per implementare un’API RESTful che recupera principalmente e serve i dati codificati JSON. Ma ho alcune situazioni in cui ho bisogno di realizzare quanto segue:

  • Esportare documenti scaricabili, come PDF, XLS, ZIP o altri file binari.
  • Recupera i dati multipart, come ad esempio JSON e un file XLS caricato

Ho un client Web basato su JQuery a pagina singola che crea chiamate AJAX a questo servizio web. Al momento, non esegue sottomissioni e utilizza GET e POST (con un object JSON). Devo utilizzare un modulo per inviare dati e un file binario allegato, oppure posso creare una richiesta multipart con file binario JSON plus?

Il livello di servizio della mia applicazione attualmente crea un object ByteArrayOutputStream quando genera un file PDF. Qual è il modo migliore per inviare questo stream al cliente via Jersey? Ho creato un MessageBodyWriter, ma non so come usarlo da una risorsa di Jersey. È l’approccio giusto?

Ho cercato tra gli esempi inclusi in Jersey, ma non ho ancora trovato nulla che illustri come fare una di queste cose. Se è importante, sto usando Jersey con Jackson per fare Object-> JSON senza il passaggio XML e non sto davvero utilizzando JAX-RS.

Sono riuscito a ottenere un file ZIP o un file PDF estendendo l’object StreamingOutput . Ecco alcuni esempi di codice:

 @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 class PDFGenerator (la mia class per la creazione del PDF) prende il stream di output dal metodo di scrittura e scrive su quello invece di un stream di output appena creato.

Non so se è il modo migliore per farlo, ma funziona.

Ho dovuto restituire un file rtf e questo ha funzionato per me.

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

Sto usando questo codice per esportare il file excel (xlsx) (Apache Poi) in jersey come allegato.

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

Ecco un altro esempio. Sto creando un QRCode come PNG tramite ByteArrayOutputStream . La risorsa restituisce un object Response e i dati del stream sono l’entity framework.

Per illustrare la gestione del codice di risposta, ho aggiunto la gestione degli header della cache ( If-modified-since , If-none-matches , ecc.).

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

Si prega di non picchiarmi nel caso stream.toByteArray() è un no-no di memoria 🙂 Funziona per i miei file PNG <1KB ...

Ho composto i miei servizi Jersey 1.17 nel seguente modo:

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 il cliente, se ne hai bisogno:

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

Questo esempio mostra come pubblicare i file di registro in JBoss tramite una risorsa di rest. Si noti che il metodo get utilizza l’interfaccia StreamingOutput per trasmettere in streaming il contenuto del file di 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(); } } 

}

Usare Jersey 2.16 Il download di file è molto semplice.

Di seguito è riportato l’esempio per il file 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(); } 

Ho trovato il seguente utile per me e ho voluto condividere nel caso in cui ti aiuta o qualcun altro. Volevo qualcosa come MediaType.PDF_TYPE, che non esiste, ma questo codice fa la stessa cosa:

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

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

Nel mio caso stavo postando un documento PDF su un altro sito:

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

Quindi p viene passato come secondo parametro da postare ().

Questo link mi è stato utile nel mettere insieme questo frammento di codice: http://jersey.576304.n2.nabble.com/Multipart-Post-td4252846.html

Questo ha funzionato bene con me 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(); } 

Un altro codice di esempio in cui è ansible caricare un file nel servizio REST, il servizio REST blocca il file e il client scarica il file zip dal server. Questo è un buon esempio di utilizzo di flussi di input e output binari usando Jersey.

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

Questa risposta è stata pubblicata da me in un’altra discussione. Spero che questo ti aiuti.