it-swarm.com.de

POST auf dem Server, PDF empfangen, an Benutzer mit jQuery übergeben

Ich habe einen Link, auf den der Benutzer klickt, um ein PDF zu erhalten. In jQuery erstelle ich einen POST - Ajax-Aufruf an den Server, um die PDF-Datei abzurufen. Die PDF enthält die korrekten Inhaltsheader usw., die normalerweise dazu führen, dass der Browser das Reader-Plugin öffnet oder dem Benutzer das Speichern der PDF-Datei ermöglicht.

Da ich den PDF mit einem Ajax-Aufruf erhalte, bin ich nicht sicher, was ich mit den Daten tun soll, die ich im OnSuccess-Callback bekomme. Wie kann ich die Daten, die ich erhalten habe, an den Browser übergeben und ihm gestatten, seine Standardeinstellung mit der Antwort PDF auszuführen?

47
bryanvick

Sie brauchen jQuery überhaupt nicht. Senden Sie Ihre POST einfach über ein Formular und fügen Sie auf der Serverseite den HTTP-Header hinzu

Content-Disposition: attachment; filename="whatever.pdf"

Der Browser führt seine Standardeinstellung aus.

Wenn Sie beim Melden von Fehlern, die während der Generierung von PDF auftreten können, vorsichtiger sein möchten, können Sie dies auch tun. POST Ihre Parameter auf Ihrem Server mit jQuery. Generieren Sie auf dem Server den binären Inhalt und zwischenspeichern Sie ihn für einige Minuten irgendwo. Sie können ihn über einen in der Benutzersitzung eingegebenen Schlüssel abrufen und eine "erfolgreiche" Ajax-Antwort an Ihre Seite zurückgeben (oder, falls ein Fehler aufgetreten ist, geben Sie einen ein "Fehler" Antwort). Wenn die Seite eine Antwort auf den Erfolg erhält, kann sie sofort Folgendes tun:

window.location = "/get/my/pdf";

Der Server gibt dann den zwischengespeicherten Inhalt PDF zurück. Stellen Sie sicher, dass der Content-Disposition-Header wie oben angegeben ist.

31
Sean

Werfen Sie einen Blick auf - jQuery Plugin für das Anfordern von Ajax-ähnlichen Datei-Downloads

Das ganze plugin besteht aus ungefähr 30 Zeilen Code (einschließlich Kommentaren).

Der Aufruf ist dem Jquery-Ajax-Aufruf ziemlich ähnlich.

$.download('/export.php','filename=myPDF&format=pdf&content=' + pdfData );

Natürlich müssen Sie den Content-Type- und Content-Disposition-Header auf der Serverseite wie für jeden solchen Download festlegen.

In Java würde ich so etwas tun

response.setContentType("application/pdf");
response.setHeader("Content-Disposition", "attachment; filename="exported.pdf");

Die Antwort, die "jQuery-Plugin für das Anfordern von Ajax-ähnlichen Dateidownloads" erwähnte, brachte mich in die richtige Richtung, aber es funktionierte nicht ganz für meine Situation, da ich ein komplexes Objekt und eine Reihe von Objekten als Suchkriterien übergeben habe. Daten filtern. Ich dachte mir, ich würde meinen Code weitergeben, falls jemand anderes in diese Situation gerät.

$.download = function (url, data, method) {
    if (url && data) {
        //convert the data object into input HTML fields
        var inputs = '';
        var convertToInput = function (key, keyStr, obj) {
            if (typeof obj === 'undefined') {
                return;
            } else if (typeof obj === "object") {
                for (var innerKey in obj) {
                    if (obj.hasOwnProperty(innerKey)) {
                        var innerKeyStr = '';
                        if (keyStr === '') {
                            innerKeyStr = innerKey.toString();
                        } else {
                            innerKeyStr = keyStr + "[" + innerKey.toString() + "]";
                        }
                        convertToInput(innerKey, innerKeyStr, obj[innerKey]);
                    }
                }
                return;
            } else if ($.isArray(obj)) {
                obj.forEach(function (item) {
                    convertToInput(key, keyStr + "[]", item);
                });
                return;
            }

            inputs += "<input type='hidden' name='" + keyStr + "' value='" + obj + "' />";
        };
        convertToInput(null, '', data);

        //send request
        jQuery('<form action="' + url + '" method="' + (method || 'post') + '">' + inputs + '</form>').appendTo('body').submit().remove();
    };
};
$.download('/api/search?format=csv', searchData, 'POST');

Es macht wahrscheinlich keinen großen Unterschied, aber um etwas Kontext zu bieten, habe ich eine Javascript- und Knockout-Benutzeroberfläche, die WebAPI, MVC4 und nHibernate aufruft. Der Teil 'format = csv' der Abfragezeichenfolge löst einen MediaTypeFormatter aus, um die zurückgegebenen Modelle in einen CSV-Dateityp zu konvertieren. Wenn ich das nicht aktiv mache, bekomme ich die Modelle von der API zurück und kann ein Slick-Gitter zur Anzeige füllen.

3
Mark Seefeldt

Ich hatte das gleiche Problem, benutze aber oben einen RESTFUL webservice und habe ein komplexes Datenobjekt, das ich posten muss.

Meine Lösung: Wie das jQuery Plugin baue ich ein temporäres Formular und übermittle es. Aber ich sende das Datenobjekt als Parameter mit json-Inhalten (ich verwende hier AngularJS, aber es sollte auch mit jQuery.param() funktionieren.)

Javascript:

$('<form target="_blank" action="' + appConstants.restbaseurl + '/print/pdf" method="POST">' + 
    "<input name='data' value='" + angular.toJson($scope.versicherung) + "' />" +
    '</form>').appendTo('body').submit().remove();

serverseitig verwenden wir einen CXF REST Service mit einem JACKSON-Provider:

Spring Config:

<jaxrs:server id="masterdataService" address="/">
    <jaxrs:serviceBeans>
        <ref bean="printRestServiceBean" />
    </jaxrs:serviceBeans>
    <jaxrs:providers>
        <bean class="org.codehaus.jackson.jaxrs.JacksonJsonProvider" />
        <bean class="de.controller.ExceptionHandler" />
    </jaxrs:providers>
</jaxrs:server>

im Controller habe ich den Parameter extrahiert und wieder in einen Java Pojo konvertiert:

package de.controller;

import javax.ws.rs.Consumes;
import javax.ws.rs.FormParam;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import org.codehaus.jackson.map.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;


@Path(Constants.PRINT_PATH)
@Consumes({ MediaType.APPLICATION_JSON, "application/x-www-form-urlencoded"})
@Produces("application/pdf; charset=UTF-8")
public class PrintRestController {

    @Autowired
    private PrintService printService;

    @POST
    @Produces("application/pdf")
    @Path("/pdf")
    public Response getPDF(@FormParam("data") String data) {
        return printService.getPDF(json2Versicherung(data));
    }

    private Versicherung json2Versicherung(String data) {
        Versicherung lVersicherung = null;
        try {
            ObjectMapper mapper = new ObjectMapper();
            lVersicherung = mapper.readValue(data, Versicherung.class);
        } catch(Exception e) {
            LOGGER.error("PrintRestController.json2Versicherung() error", e);
        }
        return lVersicherung;
    }
}

im PrintService baue ich die PDF-Binärdatei und die Antwort:

@Override
public Response getPDF(Versicherung pVersicherung) {
    byte[] result = ... //build the pdf from what ever


    ResponseBuilder response = Response.ok((Object) result);
    response.header("Content-Disposition", "inline; filename=mypdf.pdf");
    return response.build();
}

Diese Lösung funktioniert für alle Browser (auch für IE9, die keine Daten-URLs verarbeiten können) sowie für Tablets und Smartphones und hat keine Probleme mit Popup-Blockern

2
Anti-g

Das jQuery-Plugin für das Anfordern von Ajax-ähnlichen Dateidownloads besteht im Wesentlichen darin, ein Formular zu erstellen, die Postdaten als ausgeblendete Felder hinzuzufügen, sie dem Hauptteil der Seite hinzuzufügen, sie zu senden und zu entfernen. 

In meinem Fall hatte ich kein Formular, nur einen Teil der Daten, die so wie sie waren, veröffentlicht werden. Das sorgte für die folgende Lösung. Auf der Serverseite kann ich die Daten abrufen, indem ich einfach den Parameter "data" aus der Anforderung ausliesse und diese URI-Dekodierung vornehme. 

function postAndDownload(url, data) {

    encodedData = encodeURIComponent(data);

    $("<form>")
        .attr("action", url)
        .attr("method", "post")
        .append(
            $("input")
                .attr("type", "hidden")
                .attr("name", "data")
                .attr("value", encodedData)
        )
        .appendTo("body")
        .submit()
        .remove();
};
1
MichaelK

Ich verstehe nicht, warum Sie eine Ajax-Anfrage an eine Datei-Download-URL wünschen! Wenn es jedoch eher so ist, als würde der Client selbst Inhalte zum Herunterladen erzeugen - verwenden Sie einen Datenuri. Funktioniert perfekt für Chrome und Firefox 20+. Safari und IE NICHT! Wenn Flash zulässig ist, können Sie den Downloadifier verwenden.

Ah, nachdem Sie Ihren Code gelesen haben, möchten Sie eine Reihe von Parametern senden. Wenn die Abfragezeichenfolge nicht zu lang wird (IE8- hat ein Limit von 2083), verwenden Sie einfach einen Anker mit der richtigen URL. 

    $('a.export-csv').click( function (evt){
      linkEl.attr('href','/export?' + encodeURIComponent(formQueryString()));
      return true;
    });

Das Obige ermöglicht es Ihnen, die URL zu ändern, bevor das Standardereignis (der Klick) erfolgt.

0
Ustaman Sangat