首页 文章

使用JERSEY输入和输出二进制流?

提问于
浏览
108

我正在使用Jersey实现一个主要检索和提供JSON编码数据的RESTful API . 但我有一些情况需要完成以下事项:

  • 导出可下载的文档,例如PDF,XLS,ZIP或其他二进制文件 .

  • 检索多部分数据,例如一些JSON加上传的XLS文件

我有一个基于JQuery的单页Web客户端,它可以为这个Web服务创建AJAX调用 . 目前,它不进行表单提交,并使用GET和POST(使用JSON对象) . 我应该使用表单发送数据和附加的二进制文件,还是可以使用JSON plus二进制文件创建多部分请求?

我的应用程序的服务层当前在生成PDF文件时创建ByteArrayOutputStream . 通过Jersey将此流输出到客户端的最佳方法是什么?我已经创建了一个MessageBodyWriter,但我不知道如何从Jersey资源中使用它 . 这是正确的方法吗?

我一直在查看 Jersey 附带的样本,但还没有找到任何说明如何做这些事情的东西 . 如果重要的是,我正在使用泽西与 Jackson 做没有XML步骤的对象 - > JSON,并没有真正使用JAX-RS .

10 回答

  • 28

    我设法通过扩展 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方法获取输出流并写入而不是新创建的输出流 .

    不知道这是否是最佳方式,但它确实有效 .

  • 1

    我不得不返回一个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();
    
  • 22

    我正在使用此代码将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();
    
    }
    
  • 5

    这里's another example. I' m通过 ByteArrayOutputStream 创建QRCode作为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文件......

  • 7

    我一直在通过以下方式编写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();
    }
    
  • 6

    此示例显示如何通过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();
        }
    }
    

    }

  • 14

    使用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();
    }
    
  • 4

    我发现以下内容对我有帮助,我希望分享以防万一它可以帮助您或其他人 . 我想要像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

  • 108

    这对我工作正常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();
    }
    
  • 15

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

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

    这个答案是我在另一个帖子中发布的 . 希望这可以帮助 .

相关问题