2012-10-13 8 views
37

Ich versuche, eine PDF-Datei aus einem binären Stream, die ich als Antwort von einer Ajax-Anfrage erhalten.Wie PDF-Datei aus Binär-String erstellt von einem Web-Service mit Javascript

Via XmlHttpRequest erhalten i Daten folgende:

%PDF-1.4.... 
..... 
....hole data representing the file 
.... 
%% EOF 

Was ich bisher versucht, war meine Daten über Daten einzubetten: uri.

Jetzt ist nichts falsch daran. Es funktioniert gut. Leider funktioniert es nicht in IE9 und FF.

Mögliche Ursache möglicherweise FF und IE9 haben dort Probleme mit dieser Verwendung der Daten-URI.

Jetzt suche ich nach einer Lösung, die für alle Browser funktioniert.

Hier ist mein Code:

// responseText encoding 
pdfText=$.base64.decode($.trim(pdfText)); 

// Now pdfText contains %PDF-1.4 ...... data...... %%EOF 

var winlogicalname = "detailPDF"; 
var winparams = 'dependent=yes,locationbar=no,scrollbars=yes,menubar=yes,'+ 
      'resizable,screenX=50,screenY=50,width=850,height=1050'; 

var htmlText = '<embed width=100% height=100%' 
        + ' type="application/pdf"' 
        + ' src="data:application/pdf,' 
        + escape(pdfText) 
        + '"></embed>'; 

       // Open PDF in new browser window 
       var detailWindow = window.open ("", winlogicalname, winparams); 
       detailWindow.document.write (htmlText); 
       detailWindow.document.close(); 

Wie gesagt .... es funktioniert gut für Opera und Chrome (Safari nicht getestet). Mit IE oder FF wird ein leeres neues Fenster geöffnet.

Gibt es eine Lösung wie das Erstellen einer PDF-Datei im Dateisystem , damit der Benutzer es herunterladen kann?

Ich brauche eine Lösung, die in allen Browsern funktioniert zumindest in IE, FF, Opera, Chrome und Safari.

Ich habe keine Berechtigung, die Web-Service-Implementierung zu bearbeiten. Also musste es eine Lösung auf der Client-Seite sein.

Irgendwelche Ideen ???

+1

ich denke, es wäre besser für alle einen Dienst zu schreiben, die die PDF-Dateien herunterlädt, um ein Ordner, lassen Sie den Webserver die statischen Dateien dienen. Konstruieren Sie auf dem Pfad Ihre URL für die Datei. –

+0

Vereinbart mit @ SrinivasReddyThatiparthy. Laden Sie die Datei in eine temporäre Datei herunter und stellen Sie sie dem Benutzer zur Verfügung. Sie könnten es inline anzeigen, wenn dies unbedingt erforderlich ist, indem Sie eine Kombination aus Objekt- und Einbettungs-Tags verwenden - siehe diese Frage: http://stackoverflow.com/questions/1244788/embed-vs-object/1244871#1244871 – mccannf

+0

Warum müssen Sie das überhaupt tun? Daten mit js erfassen? Kannst du nicht einfach ein neues Fenster mit location = öffnen? – sander

Antwort

2

Ich habe kürzlich eine andere Frage zu diesem Thema (streaming pdf into iframe using dataurl only works in chrome).

Ich habe pdfs im ast erstellt und sie zum Browser gestreamt. Ich habe sie zuerst mit fdf erstellt, dann mit einer PDF-Klasse, die ich selbst geschrieben habe - in jedem Fall wurde die PDF aus Daten erstellt, die von einem COM-Objekt basierend auf ein paar GET-Parametern, die über die URL übergeben wurden, abgerufen wurden.

Aus dem Blick auf Ihre Daten im Ajax Anruf gesendet, sieht es aus wie Sie fast da sind. Ich habe seit ein paar Jahren nicht mehr mit dem Code gespielt und habe es nicht so gut dokumentiert, wie ich es mir gewünscht hätte, aber - ich denke, alles, was Sie tun müssen, ist das Ziel eines Iframes als URL festzulegen Du bekommst das pdf von. Obwohl dies möglicherweise nicht funktioniert - die Datei, die die PDF-Datei ausgibt, muss möglicherweise auch zuerst einen HTML-Antwortkopf ausgeben.

Auf den Punkt gebracht, ist dies der Ausgangscode I verwendet:

//We send to a browser 
header('Content-Type: application/pdf'); 
if(headers_sent()) 
    $this->Error('Some data has already been output, can\'t send PDF file'); 
header('Content-Length: '.strlen($this->buffer)); 
header('Content-Disposition: inline; filename="'.$name.'"'); 
header('Cache-Control: private, max-age=0, must-revalidate'); 
header('Pragma: public'); 
ini_set('zlib.output_compression','0'); 
echo $this->buffer; 

Also, ohne die vollständige Antworttext fro der Ajax-Aufruf da ich nicht wirklich sicher sein kann, was es ist, aber ich bin geneigt zu denken, dass der Code, der die pdf ausgibt, die Sie anfordern, möglicherweise nur das Äquivalent der letzten Zeile in dem obigen Code ist. Wenn es Code ist, den Sie kontrollieren können, würde ich versuchen, die Header zu setzen - dann kann der Browser einfach mit dem Antworttext umgehen - Sie müssen sich nicht darum kümmern, etwas zu tun.

Ich baute einfach eine URL für die PDF Ich wollte (ein Zeitplan) dann erstellt eine Zeichenfolge, die die HTML für einen iframe der gewünschten Sie, ID usw., die die konstruierte URL als es src verwendet. Sobald ich das innere html eines div auf den konstruierten html-String gesetzt habe, hat der Browser nach dem pdf gefragt und es dann angezeigt, als es empfangen wurde.

function showPdfTt(studentId) 
{ 
    var url, tgt; 
    title = byId("popupTitle"); 
    title.innerHTML = "Timetable for " + studentId; 
    tgt = byId("popupContent"); 
    url = "pdftimetable.php?"; 
    url += "id="+studentId; 
    url += "&type=Student"; 
    tgt.innerHTML = "<iframe onload=\"centerElem(byId('box'))\" src='"+url+"' width=\"700px\" height=\"500px\"></iframe>"; 
} 

EDIT: vergessen zu erwähnen - Sie können binäre pdfs auf diese Weise senden. Die Streams, die sie enthalten, müssen nicht ascii85 oder hexekodiert sein. Ich benutzte flate auf allen Streams in der pdf und es hat gut funktioniert.

4

ich änderte sich dies:

var htmlText = '<embed width=100% height=100%' 
       + ' type="application/pdf"' 
       + ' src="data:application/pdf,' 
       + escape(pdfText) 
       + '"></embed>'; 

zu

var htmlText = '<embed width=100% height=100%' 
       + ' type="application/pdf"' 
       + ' src="data:application/pdf;base64,' 
       + escape(pdfText) 
       + '"></embed>'; 

und es für mich gearbeitet.

+0

Arbeitete im Android Browser? – isobretatel

+1

Es funktioniert nicht. – Juanjo

+0

@Alexandre, Diese Lösung skaliert nicht. – Pacerier

3

Ich arbeite in PHP und benutze eine Funktion, um die binären Daten zu dekodieren, die vom Server zurückgeschickt werden. Ich extrahiere die Informationen eine Eingabe in eine einfache Datei.php und sehe die Datei über meinen Server und alle Browser zeigen das PDF-Artefakt an.

<?php 
    $data = 'dfjhdfjhdfjhdfjhjhdfjhdfjhdfjhdfdfjhdf==blah...blah...blah..' 

    $data = base64_decode($data); 
    header("Content-type: application/pdf"); 
    header("Content-Length:" . strlen($data)); 
    header("Content-Disposition: inline; filename=label.pdf"); 
    print $data; 
    exit(1); 

?> 
+4

-1. Interessant, aber nicht das, was der Benutzer gefragt hat. Er fragte speziell von einem Web-Service zu Javascript, um es auf der Client-Seite zu tun, nicht davon auszugehen, dass der Benutzer sogar den Web-Service kontrolliert oder dass er eine serverseitige Sprache verwendet. – Juanjo

2

Die Antwort von @alexandre mit base64 macht den Trick.

Die Erklärung, warum das für IE funktioniert hier ist

https://en.m.wikipedia.org/wiki/Data_URI_scheme

Unter Header 'format', wo es heißt

Einige Browser (Chrome, Opera, Safari, Firefox) eine nicht akzeptieren -Standard Reihenfolge, wenn sowohl; base64 und; charset angegeben sind, während Internet Explorer erfordert, dass die Spezifikation des Zeichensatzes dem base64-Token vorausgehen muss.

2

Sie können PDF.js verwenden, um PDF-Dateien aus Javascript zu erstellen ... es ist einfach zu codieren ... hoffe, das löst Ihre Zweifel !!!

Grüße!

18

Gibt es eine Lösung wie den Aufbau einer PDF-Datei auf Dateisystem, um der Benutzer es herunterladen zu lassen?

Try responseType von XMLHttpRequest-blob Einstellung ersetzt download Attribut bei a Element für window.open von XMLHttpRequest als .pdf Datei

var request = new XMLHttpRequest(); 
request.open("GET", "/path/to/pdf", true); 
request.responseType = "blob"; 
request.onload = function (e) { 
    if (this.status === 200) { 
     // `blob` response 
     console.log(this.response); 
     // create `objectURL` of `this.response` : `.pdf` as `Blob` 
     var file = window.URL.createObjectURL(this.response); 
     var a = document.createElement("a"); 
     a.href = file; 
     a.download = this.response.name || "detailPDF"; 
     document.body.appendChild(a); 
     a.click(); 
     // remove `a` following `Save As` dialog, 
     // `window` regains `focus` 
     window.onfocus = function() {      
      document.body.removeChild(a) 
     } 
    }; 
}; 
request.send(); 
+0

Das funktioniert perfekt für mich. Danke – Juanjo

+2

Funktioniert gut für mich auch, außer dem abschließenden 'removeChild (a)', was einen Fehler zurückgab (etwas wie "Knoten nicht gefunden"). Also habe ich diese Entfernung auf 'window.onfocus' nicht ausgelöst und stattdessen direkt nach' a.click() '' document.body.removeChild (a) 'gesetzt. – zezollo

+0

@zezollo 'window' gewinnt den Fokus vor' click' Event? Könnte wahrscheinlich durch 'document.body.appendChild (a) ersetzt werden; a.onclick = function() { window.onfocus = function() { document.body.removeChild (a); window.onfocus = null; } } a.click() ' – guest271314

2

den Browser erkennen Download Reaktion zu ermöglichen und verwenden Daten-URI für Chrome und Verwenden Sie PDF.js wie unten für andere Browser.

PDFJS.getDocument(url_of_pdf) 
.then(function(pdf) { 
    return pdf.getPage(1); 
}) 
.then(function(page) { 
    // get a viewport 
    var scale = 1.5; 
    var viewport = page.getViewport(scale); 
    // get or create a canvas 
    var canvas = ...; 
    canvas.width = viewport.width; 
    canvas.height = viewport.height; 

    // render a page 
    page.render({ 
     canvasContext: canvas.getContext('2d'), 
     viewport: viewport 
    }); 
}) 
.catch(function(err) { 
    // deal with errors here! 
}); 
8

Ich weiß, das eine ziemlich alte Frage, aber hier ist die Lösung, die ich mit heute kam:

doSomethingToRequestData().then(function(downloadedFile) { 
    // create a download anchor tag 
    var downloadLink  = document.createElement('a'); 
    downloadLink.target = '_blank'; 
    downloadLink.download = 'name_to_give_saved_file.pdf'; 

    // convert downloaded data to a Blob 
    var blob = new Blob([downloadedFile.data], { type: 'application/pdf' }); 

    // create an object URL from the Blob 
    var URL = window.URL || window.webkitURL; 
    var downloadUrl = URL.createObjectURL(blob); 

    // set object URL as the anchor's href 
    downloadLink.href = downloadUrl; 

    // append the anchor to document body 
    document.body.append(downloadLink); 

    // fire a click event on the anchor 
    downloadLink.click(); 

    // cleanup: remove element and revoke object URL 
    document.body.removeChild(downloadLink); 
    URL.revokeObjectURL(downloadUrl); 
} 
+1

Müssen Sie es mit Ajax tun? Sie können einfach die URL als href setzen und sie direkt herunterladen (aber nur wenn es eine einfache Anfrage ist) – Endless

+0

Die Verwendung eines einfachen Ankers ist in unserem Fall keine Option, AJAX war notwendig. –