Vraag In- en uitvoer van binaire streams met JERSEY?


Ik gebruik Jersey om een ​​RESTful API te implementeren die voornamelijk wordt opgehaald en wordt gebruikt voor door JSON gecodeerde gegevens. Maar ik heb een aantal situaties waarin ik het volgende moet bereiken:

  • Exporteer downloadbare documenten, zoals PDF, XLS, ZIP of andere binaire bestanden.
  • Ophalen multipart-gegevens, zoals sommige JSON plus een geüploade XLS-bestand

Ik heb een JQuery-webclient met één pagina die AJAX-oproepen naar deze webservice maakt. Op dit moment worden er geen submappen gebruikt en wordt GET en POST (met een JSON-object) gebruikt. Moet ik een formulierpost gebruiken om gegevens en een bijgevoegd binair bestand te verzenden, of kan ik een multipart-verzoek met JSON plus binair bestand maken?

De servicelaag van mijn toepassing maakt momenteel een ByteArrayOutputStream wanneer deze een PDF-bestand genereert. Wat is de beste manier om deze stream via Jersey naar de klant te sturen? Ik heb een MessageBodyWriter gemaakt, maar ik weet niet hoe ik het moet gebruiken uit een Jersey-bron. Is dat de juiste aanpak?

Ik heb de voorbeelden bekeken die bij Jersey zijn geleverd, maar ik heb nog niets gevonden dat illustreert hoe een van deze dingen te doen. Als het er toe doet, gebruik ik Jersey with Jackson om Object-> JSON te doen zonder de XML-stap en ben ik niet echt bezig met JAX-RS.


105
2017-08-16 18:42


oorsprong


antwoorden:


Ik heb een ZIP-bestand of een PDF-bestand kunnen ophalen door het StreamingOutput voorwerp. Hier is een voorbeeldcode:

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

De klasse PDFGenerator (mijn eigen klasse voor het maken van de PDF) neemt de uitvoerstroom van de schrijfmethode en schrijft daar naartoe in plaats van een nieuw gemaakte uitvoerstroom.

Ik weet niet of dit de beste manier is om het te doen, maar het werkt.


106
2017-08-17 14:48



Ik moest een RTF-bestand retourneren en dit werkte voor mij.

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

27
2017-10-22 22:34



Ik gebruik deze code om excel (xlsx) -bestand (Apache Poi) als bijlage in jersey te exporteren.

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

}

22
2018-04-02 10:59



Hier is nog een voorbeeld. Ik maak een QRCode aan als PNG via een ByteArrayOutputStream. De resource retourneert een Response object en de gegevens van de stream de entiteit zijn.

Om de afhandeling van de antwoordcode te illustreren, heb ik de afhandeling van cache-headers toegevoegd (If-modified-since, If-none-matches, enz).

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

Alsjeblieft sla me niet in de val stream.toByteArray() is een no-no geheugen verstandig :) Het werkt voor mijn <1KB PNG-bestanden ...


15
2017-09-24 21:27



Ik heb mijn diensten voor Jersey 1.17 op de volgende manier samengesteld:

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

En de klant, als u het nodig heeft:

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

13
2017-10-31 09:05



Dit voorbeeld laat zien hoe logbestanden in JBoss kunnen worden gepubliceerd via een rest-resource. Merk op dat de methode get gebruik maakt van de StreamingOutput-interface om de inhoud van het logbestand te streamen.

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

}


7
2017-07-10 16:38



Jersey 2.16 gebruiken Bestanden downloaden is heel eenvoudig.

Hieronder staat het voorbeeld voor het ZIP-bestand

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

6
2018-02-12 14:12