java füllen - Ein-und Ausgabe binärer Datenströme mit JERSEY?





tabelle erstellen (9)


Ich habe meine Jersey 1.17-Dienste wie folgt komponiert:

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

Und der Kunde, wenn Sie es brauchen:

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

Ich verwende Jersey, um eine RESTful-API zu implementieren, die hauptsächlich JSON-codierte Daten abruft und bereitstellt. Aber ich habe einige Situationen, in denen ich Folgendes erreichen muss:

  • Exportieren Sie herunterladbare Dokumente wie PDF, XLS, ZIP oder andere Binärdateien.
  • Abrufen von mehrteiligen Daten, z. B. JSON plus eine hochgeladene XLS-Datei

Ich habe einen einseitigen JQuery-basierten Webclient, der AJAX-Aufrufe für diesen Webdienst erstellt. Im Moment macht es keine Submits und verwendet GET und POST (mit einem JSON-Objekt). Soll ich einen Formularpost verwenden, um Daten und eine angehängte Binärdatei zu senden, oder kann ich eine mehrteilige Anfrage mit JSON plus Binärdatei erstellen?

Der Service-Layer meiner Anwendung erstellt derzeit einen ByteArrayOutputStream, wenn eine PDF-Datei generiert wird. Was ist der beste Weg diesen Stream über Jersey an den Kunden auszugeben? Ich habe einen MessageBodyWriter erstellt, aber ich weiß nicht, wie ich ihn aus einer Jersey-Ressource verwenden soll. Ist das der richtige Ansatz?

Ich habe mir die Beispiele angesehen, die in Jersey enthalten sind, habe aber noch nichts gefunden, was zeigt, wie man beides tut. Wenn es darauf ankommt, verwende ich Jersey mit Jackson, um Object-> JSON ohne den XML-Schritt zu machen, und verwende JAX-RS nicht wirklich.




Ich habe es geschafft, eine ZIP-Datei oder eine PDF-Datei zu erhalten, indem ich das StreamingOutput Objekt erweitere. Hier ist ein Beispielcode:

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

Die PDFGenerator-Klasse (meine eigene Klasse zum Erstellen der PDF-Datei) verwendet den Ausgabestream von der Schreibmethode und schreibt in diesen anstelle eines neu erstellten Ausgabestreams.

Ich weiß nicht, ob das der beste Weg ist, aber es funktioniert.




Ich verwende diesen Code, um Excel (xlsx) Datei (Apache Poi) in Jersey als Anhang zu exportieren.

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

}



Ich musste eine RTF-Datei zurückgeben, und das funktionierte für mich.

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



Ein weiterer Beispielcode, mit dem Sie eine Datei in den REST-Dienst hochladen können, der REST-Dienst reißt die Datei und der Client lädt die ZIP-Datei vom Server herunter. Dies ist ein gutes Beispiel für die Verwendung von binären Eingabe- und Ausgabeströmen unter Verwendung von Jersey.

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

Diese Antwort wurde von mir in einem anderen Thread gepostet. Hoffe das hilft.




Ich fand das folgende hilfreich für mich und ich wollte teilen, falls es dir oder jemand anderem hilft. Ich wollte etwas wie MediaType.PDF_TYPE, das nicht existiert, aber dieser Code macht das Gleiche:

DefaultMediaTypePredictor.CommonMediaTypes.
        getMediaTypeFromFileName("anything.pdf")

Siehe http://jersey.java.net/nonav/apidocs/1.1.0-ea/contribs/jersey-multipart/com/sun/jersey/multipart/file/DefaultMediaTypePredictor.CommonMediaTypes.html

In meinem Fall habe ich ein PDF-Dokument auf einer anderen Website veröffentlicht:

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

Dann wird p als zweiter Parameter an post () übergeben.

Dieser Link hat mir geholfen, dieses Code-Snippet zusammenzusetzen: http://jersey.576304.n2.nabble.com/Multipart-Post-td4252846.html




Mit Jersey 2.16 Datei herunterladen ist sehr einfach.

Unten ist das Beispiel für die ZIP-Datei

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



In diesem Beispiel wird gezeigt, wie Protokolldateien in JBoss über eine Ressourcenressource veröffentlicht werden. Beachten Sie, dass die get-Methode die StreamingOutput-Schnittstelle zum Streamen des Inhalts der Protokolldatei verwendet.

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

}




Die Antwort von Jonhatan ist großartig und es war sehr nützlich für mich.

Nur ein Upgrade:

Wenn Sie die Version 2.x von Jackson (zB Version 2.1) verwenden, ist die Klasse com.fasterxml.jackson.jaxrs.json.JacksonJaxbJsonProvider, daher ist die web.xml:

<init-param>
  <param-name>com.sun.jersey.config.property.packages</param-name>
  <param-value>your.project.packages;com.fasterxml.jackson.jaxrs.json</param-value>
</init-param>




java jersey jax-rs