使用JERSEYinput和输出二进制stream?
我正在使用Jersey来实现一个主要检索和提供JSON编码数据的RESTful API。 但是我有一些情况需要完成以下工作:
- 导出可下载文档,如PDF,XLS,ZIP或其他二进制文件。
- 检索多部分数据,如一些JSON加上一个上传的XLS文件
我有一个单页面的基于JQuery的Web客户端,创build对这个Web服务的AJAX调用。 目前,它不提交表单,并使用GET和POST(带有JSON对象)。 我应该利用表单发送数据和附加的二进制文件,或者我可以使用JSON加二进制文件创build多部分请求吗?
当我的应用程序的服务层生成一个PDF文件时,当前会创build一个ByteArrayOutputStream。 通过Jersey将这个stream输出到客户端的最好方法是什么? 我创build了一个MessageBodyWriter,但我不知道如何从Jersey资源中使用它。 这是正确的做法吗?
我一直在浏览泽西岛的样本,但还没有发现任何说明如何做这些事情的东西。 如果有问题的话,我和Jackson一起使用Jersey来做Object-> JSON而不用XML步骤,而不是真的使用JAX-RS。
我设法通过扩展StreamingOutput
对象来获得ZIP文件或PDF文件。 这是一些示例代码:
@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); } } }; }
PDFGenerator类(我自己的用于创buildPDF的类)从write方法获取输出stream,并写入该stream,而不是新创build的输出stream。
不知道这是否是最好的方法,但它的工作原理。
我不得不返回一个rtf文件,这对我工作。
// 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();
我使用这段代码来导出excel(xlsx)文件(Apache Poi)作为附件。
@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(); }
这是另一个例子。 我通过ByteArrayOutputStream
创build一个QRCode作为PNG。 资源返回一个Response
对象,stream的数据就是实体。
为了说明响应代码处理,我添加了caching头的处理( If-modified-since
, If-none-matches
等)。
@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; }
请不要打我的情况stream.toByteArray()
是一个不是内存明智的:)它适用于我的<1KB PNG文件…
我一直在用以下方式撰写我的Jersey 1.17服务:
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); }
而客户,如果你需要它:
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(); }
这个例子展示了如何通过rest资源在JBoss中发布日志文件。 请注意get方法使用StreamingOutput接口来传输日志文件的内容。
@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(); } }
}
使用Jersey 2.16文件下载非常简单。
下面是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(); }
我发现以下对我有帮助,我想分享的情况下,它可以帮助你或其他人。 我想要像MediaType.PDF_TYPE,这不存在,但是这个代码做同样的事情:
DefaultMediaTypePredictor.CommonMediaTypes. getMediaTypeFromFileName("anything.pdf")
在我的情况下,我正在向另一个站点发布PDF文档:
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")));
然后p作为第二个parameter passing给post()。
这个链接有助于我把这段代码片段放在一起: http : //jersey.576304.n2.nabble.com/Multipart-Post-td4252846.html
这与我工作得很好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(); }
另一个示例代码,您可以在其中将文件上载到REST服务,REST服务将压缩文件,然后客户端从服务器下载zip文件。 这是使用泽西岛使用二进制input和输出stream的一个很好的例子。
https://stackoverflow.com/a/32253028/15789
这个答案是由我发布在另一个线程。 希望这可以帮助。