2010-02-02 13 views
46

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 zu erhalten. Die PDF-Datei kommt mit den richtigen Inhaltsheadern usw., die normalerweise dazu führen, dass der Browser das Reader-Plugin öffnet oder dem Benutzer das Speichern der PDF-Datei ermöglicht.POST zu Server, PDF empfangen, an Benutzer mit jQuery senden

Da ich die PDF mit einem Ajax-Aufruf bekomme, bin ich mir nicht sicher, was ich mit den Daten tun soll, die ich im OnSuccess-Callback bekomme. Wie kann ich die Daten, die ich erhalte, dem Browser geben und zulassen, dass er seine Standardfunktion mit der PDF-Antwort ausführt?

+0

Definieren Sie 'seine Standard-Sache'. –

+0

das wäre was auch immer der Browser normalerweise mit einer Antwort mit dem Inhaltstyp auf PDF eingestellt ist. Öffnen Sie einen Leser, fragen Sie nach einem Download-Ort usw. – bryanvick

Antwort

31

Sie müssen nicht an allen jQuery. Reichen Sie einfach Ihre POST normalerweise über ein Formular, und auf der Server-Seite, fügen Sie den HTTP-Header

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

Der Browser wird die Standard tun.

Wenn Sie jedoch vorsichtiger sein möchten, wenn Fehler bei der PDF-Generierung gemeldet werden, können Sie dies tun. POST deine Parameter mit jQuery an deinen Server. Generieren Sie auf dem Server den binären Inhalt und cachen Sie ihn irgendwo für ein paar Minuten. Er ist über einen Schlüssel zugänglich, den Sie in die Benutzersitzung eingegeben haben, und gibt eine "Erfolg" -Ajax-Antwort an Ihre Seite zurück (oder wenn ein Fehler aufgetreten ist) "Fehler" Antwort). Wenn die Seite eine Erfolgsantwort zurückerhält, kann sie sofort Folgendes ausführen:

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

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

+5

Sie brauchen nicht, aber manchmal tun Sie;) –

50

Werfen Sie einen Blick auf - jQuery Plugin for Requesting Ajax-like File Downloads

Die ganze plugin ist nur etwa 30 Zeilen Code (einschließlich Kommentaren).

Der Anruf ist ziemlich ähnlich zu jQuery Ajax Anruf.

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

Ofcourse, haben Sie die Content-Type und Content-Disposition-Header auf der Serverseite zu setzen, wie Sie es für einen solchen Download bereit.

In Java würde ich so etwas wie dieses

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

Dies war die Antwort, aber ich habe nicht genug Ruf, um es zu bewerten. Ich wusste, dass ich falsch über das Problem nachdachte. – bryanvick

+0

Danke, neu hier. – bryanvick

-1

Ich denke, das beste wird sein, eine temporäre PDF-Datei im Download-Ordner zu erstellen und dann die Datei mit Pop-up mit einem iframe laden .. Chrome lädt es sofort, aber ich nehme an für andere Varianten Acrobat Reader muss installiert sein um die pdf anzusehen, aber wieder können Sie auch FlashPaper verwenden :)

+0

Die Antwort ist weit vom Kontext der Frage, und keine Details zur Verfügung gestellt –

3

Die Antwort "jQuery Plugin zum Anfordern von Ajax-like File Downloads" brachte mich in die richtige Richtung, aber es funktionierte nicht vollständig für meine Situation seit Ich habe ein komplexes Objekt und ein Array von Objekten, die als Suchkriterien/Filterdaten übergeben werden sollen. Ich dachte mir, ich würde meinen Code teilen, für den Fall, dass 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 ist wahrscheinlich nicht viel Unterschied machen, aber einige Kontext zu schaffen, habe ich den Aufruf einer Javascript und Knockout-UI bekam in WebAPI, MVC4 und nHibernate. 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 weglasse, dann bekomme ich die Modelle von der API zurück und kann ein Slick-Raster für die Anzeige auffüllen.

0

Ich verstehe nicht, warum Sie eine Ajax-Anfrage an eine Datei Download-URL wollen!Aber wenn es eher so ist, dass der Client selbst einen Inhalt zum Herunterladen generiert, verwenden Sie ein Daten-URI. Funktioniert perfekt für Chrome und Firefox 20+. Safari und IE NICHT! Wenn Flash erlaubt ist, könnten Sie downloadifier verwenden.

Ah nach dem Lesen Ihres Codes, sehe ich, dass Sie eine Reihe von Parametern senden möchten. Nun, wenn der Abfrage-String nicht zu lang wird (IE8- hat ein Limit von 2083), warum nicht einfach einen Anker mit der richtigen URL verwenden?

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

Oben können Sie die URL ändern, bevor das Standardereignis (der Klick) passiert.

+0

Ah nach dem Lesen Ihres Codes sehe ich, dass Sie eine Reihe von Parametern senden möchten. Nun, wenn der Abfrage-String nicht zu lang wird (IE8- hat ein Limit von 2083), warum nicht einfach einen Anker mit der richtigen URL verwenden? –

+0

Ihr Code sieht gut aus und ich habe etwas ähnliches versucht und es scheint zu funktionieren. Können Sie skizzieren, welcher Teil für Sie nicht funktioniert hat? –

2

Ich hatte das gleiche Problem, aber oben eine RESTFUL webservice dafür verwenden und ein komplexes Datenobjekt haben, das ich posten muss.

Meine Lösung: Wie das jQuery Plugin baue ich ein temporäres Formular und reiche es ein. Aber ich sende das Datenobjekt als Parameter mit json Inhalt (ich verwende hier AngularJS aber es sollte auch mit jQuery.param() arbeiten.)

Javascript:

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

auf der Server-Seite verwenden wir ein CXF REST Service mit einem JACKSON Provider:

Frühling 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> 

in der Steuerung i extrahieren ed die param und konvertiert sie zurück in ein Java Pojo:

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

in der Printservice i die pdf-Binär- und die Antwort bauen:

@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 (auch für IE9 für alle Browser funktioniert, wo Daten-URLs zu behandeln und auf Tablets und Smartphone und es haben keine Probleme mit Popupblocker

+0

Sie haben keine Möglichkeit, dem Benutzer mitzuteilen, dass der Download abgeschlossen ist, oder? – crush

+0

Nein, erst, wenn Sie einige zusätzliche Tricks wie z.Setzen von Cookies –

1

Das jQuery Plugin zum Anfordern von Ajax-like File Downloads ist - im Wesentlichen - Erstellen eines Formulars, Hinzufügen der Post-Daten als versteckte Feld (e) , fügen Sie es dem Hauptteil der Seite hinzu und reichen Sie es ein und es entfernen.

In meinem Fall hatte ich kein Formular, nur ein Stück Daten, wie es war. Das machte für die folgende Lösung. Auf der Serverseite kann ich die Daten erhalten, indem ich einfach den Parameter "data" aus der Anfrage lese und ihn entziffere.

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