[Java] Ein- und Ausgabe binärer Datenströme mit JERSEY?



Answers

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

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.




Hier ist ein anderes Beispiel. Ich erstelle einen QRCode als PNG über einen ByteArrayOutputStream . Die Ressource gibt ein Response Objekt zurück, und die Daten des Streams sind die Entität.

Um die Handhabung des Antwortcodes zu veranschaulichen, habe ich die Behandlung von Cache-Headern hinzugefügt ( If-modified-since , If-none-matches , usw.).

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

Bitte verprügelt mich nicht, falls stream.toByteArray() ein no-no memory stream.toByteArray() ist :) Es funktioniert für meine <1KB PNG-Dateien ...




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.




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

}




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




Links