Input and Output binary streams using JERSEY?

I'm using Jersey to implement a RESTful API that is primarily retrieve and serve JSON encoded data. But I have some situations where I need to accomplish the following:

  • Export downloadable documents, such as PDF, XLS, ZIP, or other binary files.
  • Retrieve multipart data, such some JSON plus an uploaded XLS file

I have a single-page JQuery-based web client that creates AJAX calls to this web service. At the moment, it doesn't do form submits, and uses GET and POST (with a JSON object). Should I utilize a form post to send data and an attached binary file, or can I create a multipart request with JSON plus binary file?

My application's service layer currently creates a ByteArrayOutputStream when it generates a PDF file. What is the best way to output this stream to the client via Jersey? I've created a MessageBodyWriter, but I don't know how to use it from a Jersey resource. Is that the right approach?

I've been looking through the samples included with Jersey, but haven't found anything yet that illustrates how to do either of these things. If it matters, I'm using Jersey with Jackson to do Object->JSON without the XML step and am not really utilizing JAX-RS.

149630 次浏览

通过扩展 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();

我发现以下内容对我很有帮助,我想分享一下,以防它对你或其他人有帮助。我想要一些类似 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

我使用这段代码将新泽西州的 excel (xlsx)文件(ApachePoi)作为附件导出。

@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 作为一个通过 ByteArrayOutputStream的 PNG。资源返回一个 Response对象,流的数据是实体。

为了说明响应代码处理,我添加了缓存头(If-modified-sinceIf-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 文件工作..。

这招对我很管用 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();
}

此示例演示如何通过静息资源在 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 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);
}

And the client, if you need it:

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

使用 Jersey 2.16文件下载非常简单。

Below is the example for the ZIP file

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

另一个示例代码,您可以将文件上传到 REST 服务,REST 服务压缩文件,客户机从服务器下载压缩文件。 这是使用 Jersey 使用二进制输入和输出流的一个很好的例子。

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

This answer was posted by me in another thread. Hope this helps.