我正在使用Jersey实现一个主要检索和提供JSON编码数据的RESTful API.但我有一些情况需要完成以下事项:
导出可下载的文档,例如PDF,XLS,ZIP或其他二进制文件.
检索多部分数据,例如一些JSON和上传的XLS文件
我有一个基于JQuery的单页Web客户端,它可以为这个Web服务创建AJAX调用.目前,它不进行表单提交,并使用GET和POST(使用JSON对象).我应该使用表单发送数据和附加的二进制文件,还是可以使用JSON plus二进制文件创建多部分请求?
我的应用程序的服务层当前在生成PDF文件时创建ByteArrayOutputStream.通过Jersey将此流输出到客户端的最佳方法是什么?我已经创建了一个MessageBodyWriter,但我不知道如何从Jersey资源中使用它.这是正确的方法吗?
我一直在查看泽西岛附带的样本,但还没有找到任何说明如何做这些事情的东西.如果重要的是,我正在使用泽西与杰克逊做没有XML步骤的对象 - > JSON,并没有真正使用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类(我自己的用于创建PDF的类)从write方法获取输出流并写入而不是新创建的输出流.
不知道这是否是最佳方式,但它确实有效.
我不得不返回一个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();
}
这是另一个例子.我正在创建一个QRCode作为PNG ByteArrayOutputStream
.资源返回一个Response
对象,流的数据是实体.
为了说明响应代码处理,我已经添加了缓存头(处理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")
见 http://jersey.java.net/nonav/apidocs/1.1.0-ea/contribs/jersey-multipart/com/sun/jersey/multipart/file/DefaultMediaTypePredictor.CommonMediaTypes.html
就我而言,我将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作为第二个参数传递给post().
这个链接对我将这段代码片段放在一起很有帮助:http: //jersey.576304.n2.nabble.com/Multipart-Post-td4252846.html