저는 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를 사용하지 않습니다.
답변
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();
답변
이 코드를 사용하여 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();
}
답변
여기 또 다른 예가 있습니다. 을 통해 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();
}
답변
이 예제는 나머지 리소스를 통해 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();
}