JERSEY를 사용하는 입력 및 출력 바이너리 스트림?


111

저는 Jersey를 사용하여 주로 JSON 인코딩 데이터를 검색하고 제공하는 RESTful API를 구현하고 있습니다. 그러나 다음을 수행해야하는 몇 가지 상황이 있습니다.

  • PDF, XLS, ZIP 또는 기타 바이너리 파일과 같은 다운로드 가능한 문서를 내 보냅니다.
  • 일부 JSON 및 업로드 된 XLS 파일과 같은 멀티 파트 데이터 검색

이 웹 서비스에 대한 AJAX 호출을 생성하는 단일 페이지 JQuery 기반 웹 클라이언트가 있습니다. 현재 양식 제출을 수행하지 않고 GET 및 POST (JSON 객체 사용)를 사용합니다. 데이터 및 첨부 된 이진 파일을 보내기 위해 양식 게시물을 사용해야합니까, 아니면 JSON과 이진 파일이 포함 된 다중 요청을 만들 수 있습니까?

내 애플리케이션의 서비스 레이어는 현재 PDF 파일을 생성 할 때 ByteArrayOutputStream을 생성합니다. Jersey를 통해이 스트림을 클라이언트에 출력하는 가장 좋은 방법은 무엇입니까? MessageBodyWriter를 만들었지 만 Jersey 리소스에서 사용하는 방법을 모르겠습니다. 이것이 올바른 접근 방식입니까?

Jersey에 포함 된 샘플을 살펴 보았지만 아직 이러한 작업을 수행하는 방법을 보여주는 어떤 것도 찾지 못했습니다. 중요한 경우 Jackson과 Jersey를 사용하여 XML 단계없이 Object-> JSON을 수행하고 실제로 JAX-RS를 사용하지 않습니다.

답변:


109

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 메서드에서 출력 스트림을 가져 와서 새로 생성 된 출력 스트림 대신에 씁니다.

그것이 최선의 방법인지는 모르지만 작동합니다.


33
StreamingOutput을 개체의 엔터티로 반환 할 수도 있습니다 Response. 이렇게하면 미디어 유형, HTTP 응답 코드 등을 쉽게 제어 할 수 있습니다. 코드를 게시하려면 알려주세요.
Hank

3
@MyTitle : 예제
Hank

3
이 스레드의 코드 예제를 참조로 사용했으며 클라이언트가 출력을 안정적으로 수신하려면 StreamingOutput.write ()에서 OutputStream을 플러시해야한다는 것을 알았습니다. 그렇지 않으면 로그에 StreamingOutput이 실행 중이라고 말 했음에도 불구하고 헤더에 "Content-Length : 0"이 표시되고 본문이 표시되지 않습니다.
Jon Stewart

@JonStewart-저는 generatePDF 메서드 내에서 플러시를하고 있다고 생각합니다.
MikeTheReader jul.

1
@ Dante617. Jersey 클라이언트가 서버에 바이너리 스트림을 보내는 방법 (jersey 2.x 사용) 클라이언트 측 코드를 게시 하시겠습니까?
Débora

29

나는 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();

26
출력이 완전히 준비된 후에 만 ​​전송되기 때문에 좋지 않습니다. byte []는 스트림이 아닙니다.
java.is.for.desktop

7
이는 모든 바이트를 메모리로 소비하므로 대용량 파일로 인해 서버가 다운 될 수 있습니다. 스트리밍의 목적은 모든 바이트를 메모리로 소비하지 않도록하는 것입니다.
Robert Christian

22

이 코드를 사용하여 jersey의 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();

}

15

여기 또 다른 예가 있습니다. 을 통해 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 파일에서 작동합니다 ...


6
출력에 반환 된 객체가 스트림이 아닌 바이트 배열이기 때문에 잘못된 스트리밍 예제라고 생각합니다.
AlikElzin-kilaka

스트림에 대한 좋은 예가 아니라 GET 리소스 요청에 대한 응답을 빌드하는 좋은 예입니다. 이것은 전혀 스트림이 아닙니다.
Robert Christian

14

다음과 같은 방식으로 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();
}

7

이 예제는 나머지 리소스를 통해 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();
    }
}    

}


1
참고로, 파이프 방법 대신 Apache commons I / O에서 IOUtils.copy를 사용할 수도 있습니다.
David

7

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

1
매력처럼 작동합니다. 이 스트리밍에 대해 잘 모르겠습니다.
Oliver

1
Jersey를 사용한다면 가장 쉬운 방법입니다. 감사합니다
ganchito55

@GET 대신 @POST로 할 수 있습니까?
spr

@spr 네, 가능하다고 생각합니다. 서버 페이지 응답, 그것은 다운로드 창을 제공해야 할 때
orangegiraffa

5

다음 내용이 도움이되었으며 귀 하나 다른 사람에게 도움이 될 경우 공유하고 싶었습니다. 존재하지 않는 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 코드 조각을 모으는 데 도움이되었습니다.


4

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

1
확실하지 않습니다. Response.ok("file path null").build();정말 괜찮습니까? 아마도 다음과 같은 것을 사용해야합니다Response.status(Status.BAD_REQUEST).entity(...
Christophe Roussy 2015

1

REST 서비스에 파일을 업로드 할 수있는 또 다른 샘플 코드는 REST 서비스가 파일을 압축하고 클라이언트가 서버에서 zip 파일을 다운로드합니다. 이것은 Jersey를 사용하여 이진 입력 및 출력 스트림을 사용하는 좋은 예입니다.

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

이 답변은 내가 다른 스레드에 게시했습니다. 도움이 되었기를 바랍니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.