Laden Sie die PDF-Datei mit Ajax herunter und öffnen Sie sie

98

Ich habe eine Aktionsklasse, die ein PDF generiert. Das contentTypeist entsprechend eingestellt.

public class MyAction extends ActionSupport 
{
   public String execute() {
    ...
    ...
    File report = signedPdfExporter.generateReport(xyzData, props);

    inputStream = new FileInputStream(report);
    contentDisposition = "attachment=\"" + report.getName() + "\"";
    contentType = "application/pdf";
    return SUCCESS;
   }
}

Ich rufe dies action über einen Ajax-Anruf an. Ich weiß nicht, wie ich diesen Stream an den Browser senden kann. Ich habe ein paar Dinge ausprobiert, aber nichts hat funktioniert.

$.ajax({
    type: "POST",
    url: url,
    data: wireIdList,
    cache: false,
    success: function(response)
    {
        alert('got response');
        window.open(response);
    },
    error: function (XMLHttpRequest, textStatus, errorThrown) 
    {
        alert('Error occurred while opening fax template' 
              + getAjaxErrorString(textStatus, errorThrown));
    }
});

Das obige gibt den Fehler:

Ihr Browser hat eine Anfrage gesendet, die dieser Server nicht verstehen konnte.

Nayn
quelle

Antworten:

37

Sie brauchen dafür nicht unbedingt Ajax. Nur ein <a>Link genügt , wenn Sie den Satz content-dispositionauf attachmentdem Server - Seite Code. Auf diese Weise bleibt die übergeordnete Seite nur geöffnet, wenn dies Ihr Hauptanliegen war (warum hätten Sie sich sonst unnötig für Ajax entschieden?). Außerdem gibt es keine Möglichkeit, dies gut akynchron zu handhaben. PDF sind keine Zeichendaten. Es sind binäre Daten. Du kannst so etwas nicht machen $(element).load(). Sie möchten hierfür eine völlig neue Anfrage verwenden. Dafür <a href="pdfservlet/filename.pdf">pdf</a>ist es perfekt geeignet.

Um Sie besser mit dem serverseitigen Code zu unterstützen, müssen Sie mehr über die verwendete Sprache erzählen und einen Auszug der Codeversuche veröffentlichen.

BalusC
quelle
7
Noch einmal: Sie nicht brauchen Ajax dafür. Es bittet nur um Ärger. PDF sind Binärdaten, keine Zeichendaten wie HTML oder JSON.
BalusC
3
var url = contextPath + "/xyz/blahBlah.action"; url + = url + "?" + params; try {var child = window.open (url); child.focus (); } catch (e) {}
Nayn
5
In einigen Browsern bleibt window.open offen und leer, was für Endbenutzer ärgerlich sein kann. Verwenden Sie daher auch NICHT window.open. Wenn das auf eingestellt content-dispositionist attachment, erhalten Sie nur einen Save asDialog. Die übergeordnete Seite bleibt unverändert. Nur <a href="pdfservlet/filename.pdf">pdf</a>oder a <form action="pdfservlet/filename.pdf"><input type="submit"></form>ist mehr als genug.
BalusC
5
Es gibt eine begrenzte URL-Länge. Und der Autor fragt nach POST.
Edward Olamisan
3
Stimmen Sie mit @EdwardOlamisan überein, dies ist keine korrekte Antwort, da der Autor versucht hat, POSTDaten zu erstellen.
Adamj
122

Hier ist, wie ich das zum Laufen gebracht habe

$.ajax({
  url: '<URL_TO_FILE>',
  success: function(data) {
    var blob=new Blob([data]);
    var link=document.createElement('a');
    link.href=window.URL.createObjectURL(blob);
    link.download="<FILENAME_TO_SAVE_WITH_EXTENSION>";
    link.click();
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Aktualisierte Antwort mit download.js

$.ajax({
  url: '<URL_TO_FILE>',
  success: download.bind(true, "<FILENAME_TO_SAVE_WITH_EXTENSION>", "<FILE_MIME_TYPE>")
});

Mayur Padshala
quelle
29
Funktioniert es auf Chrom? Ich kann nur ein leeres PDF sehen.
Tarun Gupta
1
Ja, es funktioniert in allen modernen Browsern. Wenn Sie ein leeres PDF sehen, versuchen Sie, die Ajax-URL in einem neuen Tab auszuführen. Wenn dort auch ein leerer Bildschirm angezeigt wird, liegt möglicherweise ein Problem mit dem PDF selbst vor. Wenn Sie dort eine PDF-Datei sehen und nicht in der heruntergeladenen Datei, lassen Sie es mich in meiner E-Mail wissen. :)
Mayur Padshala
5
Dies (Ankerelement) funktionierte bei IE 11, Edge und Firefox eigentlich nicht. Es hat funktioniert, den Erfolg auf "window.open (URL.createObjectURL (blob))" zu ändern.
JimiSweden
3
Die PDF-Datei wird heruntergeladen, es ist jedoch kein Inhalt verfügbar. Ich habe Byte [] auf der Serverseite gespeichert und PDF-Inhalt ist verfügbar. PLZ vorschlagen.
Awanish Kumar
4
leere pdf-Datei wird heruntergeladen.
Farukh
31

Ich glaube nicht, dass eine der früheren Antworten das Problem des Originalplakats aufgedeckt hat. Sie alle setzen eine GET-Anfrage voraus, während das Poster versuchte, Daten zu POSTEN und als Antwort einen Download zu erhalten.

Auf der Suche nach einer besseren Antwort haben wir dieses jQuery-Plugin zum Anfordern von Ajax-ähnlichen Dateidownloads gefunden .

In seinem "Herzen" erstellt es ein "temporäres" HTML-Formular, das die angegebenen Daten als Eingabefelder enthält. Dieses Formular wird an das Dokument angehängt und an die gewünschte URL gesendet. Gleich danach wird das Formular wieder entfernt:

jQuery('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
    .appendTo('body').submit().remove()

Update Mayurs Antwort sieht im Vergleich zu dem von mir genannten jQuery-Plug-In ziemlich vielversprechend und sehr einfach aus.

Chiccodoro
quelle
9

So löse ich dieses Problem.
Die Antwort von Jonathan Amend auf diesen Beitrag hat mir sehr geholfen.
Das folgende Beispiel ist vereinfacht.

Für weitere Details kann der obige Quellcode eine Datei mithilfe einer JQuery Ajax-Anforderung (GET, POST, PUT usw.) herunterladen . Es hilft auch, Parameter als JSON hochzuladen und den Inhaltstyp in application / json (meine Standardeinstellung) zu ändern .

Die HTML- Quelle:

<form method="POST">
    <input type="text" name="startDate"/>
    <input type="text" name="endDate"/>
    <input type="text" name="startDate"/>
    <select name="reportTimeDetail">
        <option value="1">1</option>
    </select>
    <button type="submit"> Submit</button>
</form>  

Ein einfaches Formular mit zwei Eingabetexten, einem Auswahl- und einem Schaltflächenelement.

Die Quelle der Javascript-Seite :

<script type="text/javascript" src="JQuery 1.11.0 link"></script>
<script type="text/javascript">
    // File Download on form submition.
    $(document).on("ready", function(){
        $("form button").on("click", function (event) {
            event.stopPropagation(); // Do not propagate the event.

            // Create an object that will manage to download the file.
            new AjaxDownloadFile({
                url: "url that returns a file",
                data: JSON.stringify($("form").serializeObject())
            });

            return false; // Do not submit the form.
        });
    });
</script>  

Ein einfaches Ereignis beim Klicken auf die Schaltfläche. Es wird ein AjaxDownloadFile-Objekt erstellt. Die AjaxDownloadFile-Klassenquelle befindet sich unten.

Die AjaxDownloadFile-Klassenquelle :

var AjaxDownloadFile = function (configurationSettings) {
    // Standard settings.
    this.settings = {
        // JQuery AJAX default attributes.
        url: "",
        type: "POST",
        headers: {
            "Content-Type": "application/json; charset=UTF-8"
        },
        data: {},
        // Custom events.
        onSuccessStart: function (response, status, xhr, self) {
        },
        onSuccessFinish: function (response, status, xhr, self, filename) {
        },
        onErrorOccured: function (response, status, xhr, self) {
        }
    };
    this.download = function () {
        var self = this;
        $.ajax({
            type: this.settings.type,
            url: this.settings.url,
            headers: this.settings.headers,
            data: this.settings.data,
            success: function (response, status, xhr) {
                // Start custom event.
                self.settings.onSuccessStart(response, status, xhr, self);

                // Check if a filename is existing on the response headers.
                var filename = "";
                var disposition = xhr.getResponseHeader("Content-Disposition");
                if (disposition && disposition.indexOf("attachment") !== -1) {
                    var filenameRegex = /filename[^;=\n]*=(([""]).*?\2|[^;\n]*)/;
                    var matches = filenameRegex.exec(disposition);
                    if (matches != null && matches[1])
                        filename = matches[1].replace(/[""]/g, "");
                }

                var type = xhr.getResponseHeader("Content-Type");
                var blob = new Blob([response], {type: type});

                if (typeof window.navigator.msSaveBlob !== "undefined") {
                    // IE workaround for "HTML7007: One or more blob URLs were revoked by closing the blob for which they were created. These URLs will no longer resolve as the data backing the URL has been freed.
                    window.navigator.msSaveBlob(blob, filename);
                } else {
                    var URL = window.URL || window.webkitURL;
                    var downloadUrl = URL.createObjectURL(blob);

                    if (filename) {
                        // Use HTML5 a[download] attribute to specify filename.
                        var a = document.createElement("a");
                        // Safari doesn"t support this yet.
                        if (typeof a.download === "undefined") {
                            window.location = downloadUrl;
                        } else {
                            a.href = downloadUrl;
                            a.download = filename;
                            document.body.appendChild(a);
                            a.click();
                        }
                    } else {
                        window.location = downloadUrl;
                    }

                    setTimeout(function () {
                        URL.revokeObjectURL(downloadUrl);
                    }, 100); // Cleanup
                }

                // Final custom event.
                self.settings.onSuccessFinish(response, status, xhr, self, filename);
            },
            error: function (response, status, xhr) {
                // Custom event to handle the error.
                self.settings.onErrorOccured(response, status, xhr, self);
            }
        });
    };
    // Constructor.
    {
        // Merge settings.
        $.extend(this.settings, configurationSettings);
        // Make the request.
        this.download();
    }
};

Ich habe diese Klasse erstellt, um sie meiner JS-Bibliothek hinzuzufügen. Es ist wiederverwendbar. Hoffentlich hilft das.

George Siggouroglou
quelle
2
BlobObjekt wird in IE10 + unterstützt.
Crush
Ich musste das responseTypevon xhr auf arraybufferoder setzen, blobdamit dies funktioniert. (Ansonsten funktioniert das
super
Ich hatte genau die gleiche Frage. Alle Leute, die antworten "mach es einfach zu einem Link", helfen dem OP nicht. Wenn Ihr Inhalt dynamisch ist und der Link, zu dem Sie gehen, dynamisch ist, müssen Sie alles abfragen ... Die Antwort für mich war, ein Formular mit allen versteckten Eingaben auf die Seite zu setzen (wird dem Benutzer nicht angezeigt) und Füllen Sie es dann aus und senden Sie es mit jquery. Funktioniert super.
Scott
Dies ist eine großartige Antwort, aber aus irgendeinem Grund bekomme ich immer wieder leere PDF-Dateien. Kann es nicht herausfinden. Wenn ich dasselbe Byteset über die API zurückgebe, ist das in Ordnung, also hat es etwas mit der MVC-Antwort zu tun. Ich verwende den Antworttyp FileResult: File (Bytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
Jurijs Kastanovs
Erläuterung: Wenn die URL über die Adressleiste geöffnet wird, wird die Datei korrekt geöffnet. Wenn ich AJAX + Blob verwende, um die Datei zu erhalten, ist die Datei fehlerhaft.
Jurijs Kastanovs
7

Was für mich funktioniert hat, ist der folgende Code, während die Serverfunktion abruft File(memoryStream.GetBuffer(), "application/pdf", "fileName.pdf");:

$http.get( fullUrl, { responseType: 'arraybuffer' })
            .success(function (response) {
                var blob = new Blob([response], { type: 'application/pdf' });

                if (window.navigator && window.navigator.msSaveOrOpenBlob) {
                    window.navigator.msSaveOrOpenBlob(blob); // for IE
                }
                else {
                    var fileURL = URL.createObjectURL(blob);
                    var newWin = window.open(fileURL);
                    newWin.focus();
                    newWin.reload();
                }
});
ParPar
quelle
Dies funktioniert für mich zum Zeitpunkt dieses Kommentars und des neuesten Chrome
Loredra L
6

Sie können dieses Plugin verwenden, das ein Formular erstellt, es sendet und dann von der Seite entfernt.

jQuery.download = function(url, data, method) {
    //url and data options required
    if (url && data) {
        //data can be string of parameters or array/object
        data = typeof data == 'string' ? data : jQuery.param(data);
        //split params into form inputs
        var inputs = '';
        jQuery.each(data.split('&'), function() {
            var pair = this.split('=');
            inputs += '<input type="hidden" name="' + pair[0] +
                '" value="' + pair[1] + '" />';
        });
        //send request
        jQuery('<form action="' + url +
                '" method="' + (method || 'post') + '">' + inputs + '</form>')
            .appendTo('body').submit().remove();
    };
};


$.download(
    '/export.php',
    'filename=mySpreadsheet&format=xls&content=' + spreadsheetData
);

Das hat bei mir funktioniert. Fand dieses Plugin hier

Ijas Ameenudeen
quelle
Dieses Plugin erstellt lediglich ein Formular, sendet es und entfernt es dann von der Seite. (wenn sich jemand wunderte)
Crush
4

Der folgende Code hat bei mir funktioniert

//Parameter to be passed
var data = 'reportid=R3823&isSQL=1&filter=[]';
var xhr = new XMLHttpRequest();
xhr.open("POST", "Reporting.jsp"); //url.It can pdf file path
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.responseType = "blob";
xhr.onload = function () {
    if (this.status === 200) {
        var blob = new Blob([xhr.response]);
        const url = window.URL.createObjectURL(blob);
        var a = document.createElement('a');
        a.href = url;
        a.download = 'myFile.pdf';
        a.click();
        setTimeout(function () {
            // For Firefox it is necessary to delay revoking the ObjectURL
            window.URL.revokeObjectURL(data)
                , 100
        })
    }
};
xhr.send(data);
MemZ
quelle
4

Um das leere PDF-Problem in der Post-Anfrage zu beheben und Stream-Daten wie PDF abzurufen, müssen Sie den Antworttyp als "Array-Puffer" oder "Blob" in der Anfrage hinzufügen

$.ajax({
  url: '<URL>',
  type: "POST",
  dataType: 'arraybuffer',
  success: function(data) {
    let blob = new Blob([data], {type: 'arraybuffer'});
    let link = document.createElement('a');
    let objectURL = window.URL.createObjectURL(blob);
    link.href = objectURL;
    link.target = '_self';
    link.download = "fileName.pdf";
    (document.body || document.documentElement).appendChild(link);
    link.click();
    setTimeout(()=>{
        window.URL.revokeObjectURL(objectURL);
        link.remove();
    }, 100);
  }
});
Ninja
quelle
3

In Bezug auf die Antwort von Mayur Padshala ist dies die richtige Logik, um eine PDF-Datei über Ajax herunterzuladen. Wie andere in den Kommentaren berichten, wird bei dieser Lösung tatsächlich ein leeres PDF heruntergeladen.

Der Grund hierfür liegt in der akzeptierten Antwort auf diese erklärt Frage : jQuery hat einige Probleme Laden Binärdaten AJAX - Anforderungen verwenden, da es noch nicht , einige HTML5 XHR v2 - Funktionen zu implementieren, diese Erweiterung sehen Anforderung und diese Diskussion .

Die Verwendung HTMLHTTPRequestdes Codes sollte also folgendermaßen aussehen:

var req = new XMLHttpRequest();
req.open("POST", "URL", true);
req.responseType = "blob";
req.onload = function (event) {
    var blob = req.response;
    var link=document.createElement('a');
    link.href=window.URL.createObjectURL(blob);
    link.download="name_for_the_file_to_save_with_extention";
    link.click();
}
Vpant
quelle
2

Erstellen Sie einen versteckten Iframe und dann in Ihrem Ajax-Code oben:

url: document.getElementById('myiframeid').src = your_server_side_url ,

und entfernen Sie die window.open(response);

Qalhat
quelle
Diese Lösung wirkte wie ein Zauber. Ich rufe ein serverseitiges Skript auf, das einen Curl-Aufruf an einen Dienst ausführt, der die Datei über Curl abruft. Dies funktioniert hervorragend, da ich ein Lade-GIF löschen und den Anforderungslink deaktivieren kann.
Eggmatters
1
Diese Lösung funktioniert für GET-Anforderungen, nicht für POST-Anforderungen wie im ursprünglichen Beitrag.
Chiccodoro
2

Dieses Snippet ist für eckige js-Benutzer gedacht, bei denen das gleiche Problem auftritt. Beachten Sie, dass die Antwortdatei mithilfe eines programmierten Klickereignisses heruntergeladen wird. In diesem Fall wurden die Header vom Server gesendet, der Dateiname und Inhalt / Typ enthielt.

$http({
    method: 'POST', 
    url: 'DownloadAttachment_URL',
    data: { 'fileRef': 'filename.pdf' }, //I'm sending filename as a param
    headers: { 'Authorization': $localStorage.jwt === undefined ? jwt : $localStorage.jwt },
    responseType: 'arraybuffer',
}).success(function (data, status, headers, config) {
    headers = headers();
    var filename = headers['x-filename'];
    var contentType = headers['content-type'];
    var linkElement = document.createElement('a');
    try {
        var blob = new Blob([data], { type: contentType });
        var url = window.URL.createObjectURL(blob);

        linkElement.setAttribute('href', url);
        linkElement.setAttribute("download", filename);

        var clickEvent = new MouseEvent("click", {
            "view": window,
            "bubbles": true,
            "cancelable": false
        });
        linkElement.dispatchEvent(clickEvent);
    } catch (ex) {
        console.log(ex);
    }
}).error(function (data, status, headers, config) {
}).finally(function () {

});
Gihan Sandaru
quelle
Bitte schreiben Sie eine Erklärung für Ihre Antwort.
Gufran Hasan
1

Müssen Sie es mit Ajax machen? Könnte es nicht eine Möglichkeit sein, es in einen Iframe zu laden?

Emil Vikström
quelle
1
Ich überprüfe, ob dies mit Ajax möglich ist. Wenn es technisch unmöglich oder ein minderwertiger Ansatz ist, würde ich zu anderen Ansätzen wechseln.
Nayn
1

Ich hoffe, dies erspart Ihnen ein paar Stunden und erspart Ihnen Kopfschmerzen. Es hat eine Weile gedauert, bis ich das herausgefunden habe, aber die reguläre Anfrage von $ .ajax () hat meine PDF-Datei ruiniert, während die Anfrage über die Adressleiste perfekt funktioniert hat. Die Lösung war folgende:

Fügen Sie download.js hinzu: http://danml.com/download.html

Verwenden Sie dann XMLHttpRequest anstelle der Anforderung $ .ajax ().

    var ajax = new XMLHttpRequest(); 

    ajax.open("GET", '/Admin/GetPdf' + id, true); 
    ajax.onreadystatechange = function(data) { 
        if (this.readyState == 4)
        {
            if (this.status == 200)
            {
                download(this.response, "report.pdf", "application/pdf");

            }
            else if (this.responseText != "")
            {
                alert(this.responseText);
            }
        }
        else if (this.readyState == 2)
        {
            if (this.status == 200)
            {
                this.responseType = "blob";
            }
            else
            {
                this.responseType = "text";
            }
        }
    };

    ajax.send(null);
Jurijs Kastanovs
quelle
0

var xhr;
var beforeSend = function(){
    $('#pleasewaitDL').modal('show');
}
$(function () {
    $('#print_brochure_link').click(function(){
        beforeSend();
        xhr = new XMLHttpRequest();
        xhr.open("GET",$('#preparedPrintModalForm').attr('action'), true); 
        xhr.responseType = "blob";
        xhr.onload = function (e) {
            if (this.status === 200) {
                var file = window.URL.createObjectURL(this.response);
                var a = document.createElement("a");
                a.href = file;
                a.download = this.response.name || "Property Brochure";
                console.log(file);
                document.body.appendChild(a);
                a.click();
                
                window.onfocus = function () {                     
                  document.body.removeChild(a)
                }
                $('#pleasewaitDL').modal('hide');
            };
        };
        xhr.send($('#preparedPrintModalForm').serialize());
    });
    $('#pleasewaitDLCancel').click(function() {
        xhr.abort();
    });
});

POGSNET
quelle
0

Wenn Sie wie wir mit Dateistreams arbeiten müssen (also keine physisch gespeicherten PDF-Dateien) und die PDF-Datei ohne erneutes Laden der Seite herunterladen möchten, funktioniert die folgende Funktion für uns:

HTML

<div id="download-helper-hidden-container" style="display:none">
     <form id="download-helper-form" target="pdf-download-output" method="post">
            <input type="hidden" name="downloadHelperTransferData" id="downloadHelperTransferData" />
     </form>
     <iframe id="pdf-helper-output" name="pdf-download-output"></iframe>
</div>

Javascript

var form = document.getElementById('download-helper-form');
$("#downloadHelperTransferData").val(transferData);
form.action = "ServerSideFunctionWhichWritesPdfBytesToResponse";
form.submit();

Aufgrund des Ziels = "pdf-download-output" wird die Antwort in den iframe geschrieben und daher wird kein erneutes Laden der Seite ausgeführt, sondern der pdf-response-stream wird im Browser als Download ausgegeben.

George Maharis
quelle
Entschuldigung, aber wie erhalten Sie den Wert von transferData?
Kate