486 Stimmen

Datei-Download von Ajax-Post verarbeiten

Ich habe eine JavaScript-App, die Ajax POST-Anforderungen an eine bestimmte URL sendet. Die Antwort kann ein JSON-String sein oder es kann sich um eine Datei handeln (als Anhang). Ich kann problemlos Content-Type und Content-Disposition in meinem Ajax-Aufruf erkennen, aber sobald ich feststelle, dass die Antwort eine Datei enthält, wie kann ich dem Client anbieten, sie herunterzuladen? Ich habe eine Reihe ähnlicher Threads hier gelesen, aber keiner von ihnen liefert die Antwort, nach der ich suche.

Bitte, bitte, bitte posten Sie keine Antworten, die vorschlagen, dass ich Ajax nicht verwenden sollte oder den Browser umleiten sollte, denn das ist keine Option. Die Verwendung eines einfachen HTML-Formulars ist ebenfalls keine Option. Was ich brauche, ist, dem Client einen Download-Dialog anzuzeigen. Kann dies gemacht werden und wie?

661voto

Jonathan Amend Punkte 12273

Gib nicht so schnell auf, denn dies ist möglich (in modernen Browsern) unter Verwendung von Teilen der FileAPI:

var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.responseType = 'blob';
xhr.onload = function () {
    if (this.status === 200) {
        var blob = this.response;
        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, '');
        }

        if (typeof window.navigator.msSaveBlob !== 'undefined') {
            // IE-Workaround für "HTML7007: Eine oder mehrere Blob-URLs wurden durch Schließen des Blob, für den sie erstellt wurden, widerrufen. Diese URLs werden nicht mehr aufgelöst, da die Daten entfernt wurden, auf die die URL hinweist."
            window.navigator.msSaveBlob(blob, filename);
        } else {
            var URL = window.URL || window.webkitURL;
            var downloadUrl = URL.createObjectURL(blob);

            if (filename) {
                // Verwendung des HTML5 a[download]-Attributs zur Angabe des Dateinamens
                var a = document.createElement("a");
                // Safari unterstützt dies noch nicht
                if (typeof a.download === 'undefined') {
                    window.location.href = downloadUrl;
                } else {
                    a.href = downloadUrl;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                }
            } else {
                window.location.href = downloadUrl;
            }

            setTimeout(function () { URL.revokeObjectURL(downloadUrl); }, 100); // Bereinigung
        }
    }
};
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send($.param(params, true));

Oder bei Verwendung von jQuery.ajax:

$.ajax({
    type: "POST",
    url: url,
    data: params,
    xhrFields: {
        responseType: 'blob' // um zu verhindern, dass binäre Daten bei Zeichensatzkonvertierung beschädigt werden
    },
    success: function(blob, status, xhr) {
        // Überprüfung auf einen Dateinamen
        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, '');
        }

        if (typeof window.navigator.msSaveBlob !== 'undefined') {
            // IE-Workaround für "HTML7007: Eine oder mehrere Blob-URLs wurden durch Schließen des Blob, für den sie erstellt wurden, widerrufen. Diese URLs werden nicht mehr aufgelöst, da die Daten entfernt wurden, auf die die URL hinweist."
            window.navigator.msSaveBlob(blob, filename);
        } else {
            var URL = window.URL || window.webkitURL;
            var downloadUrl = URL.createObjectURL(blob);

            if (filename) {
                // Verwendung des HTML5 a[download]-Attributs zur Angabe des Dateinamens
                var a = document.createElement("a");
                // Safari unterstützt dies noch nicht
                if (typeof a.download === 'undefined') {
                    window.location.href = downloadUrl;
                } else {
                    a.href = downloadUrl;
                    a.download = filename;
                    document.body.appendChild(a);
                    a.click();
                }
            } else {
                window.location.href = downloadUrl;
            }

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

125voto

Erstellen Sie ein Formular, verwenden Sie die POST-Methode, senden Sie das Formular ab - ein iframe ist nicht erforderlich. Wenn die Serverseite auf die Anfrage antwortet, schreiben Sie einen Antwortheader für den Dateityp der Datei, und es wird einen Download-Dialog anzeigen - das habe ich schon einige Male gemacht.

Sie möchten den Inhaltstyp application/download - suchen Sie einfach danach, wie Sie einen Download für die jeweilige Sprache bereitstellen.

50voto

Naren Yellavula Punkte 6408

Ich hatte dasselbe Problem und konnte es erfolgreich lösen. Dies ist mein Anwendungsfall.

"JSON-Daten an den Server senden und eine Excel-Datei empfangen. Diese Excel-Datei wird vom Server erstellt und als Antwort an den Client zurückgegeben. Diese Antwort im Browser als Datei mit benutzerdefiniertem Namen herunterladen"

$("#my-button").on("click", function(){

// Daten zum Posten
data = {
    ids: [1, 2, 3, 4, 5]
};

// Verwenden Sie XMLHttpRequest anstelle von Jquery $ajax
xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
    var a;
    if (xhttp.readyState === 4 && xhttp.status === 200) {
        // Trick für einen herunterladbaren Link
        a = document.createElement('a');
        a.href = window.URL.createObjectURL(xhttp.response);
        // Geben Sie den gewünschten Dateinamen für den Download an
        a.download = "test-file.xls";
        a.style.display = 'none';
        document.body.appendChild(a);
        a.click();
    }
};
// Daten an die URL posten, die den Post-Request bearbeitet
xhttp.open("POST", excelDownloadUrl);
xhttp.setRequestHeader("Content-Type", "application/json");
// Sie sollten die Antworttyp als Blob für binäre Antworten festlegen
xhttp.responseType = 'blob';
xhttp.send(JSON.stringify(data));
});

In dem obigen Auszug passiert Folgendes

  • Ein Array als JSON mithilfe von XMLHttpRequest an den Server senden.
  • Nachdem der Inhalt als Blob (binär) abgerufen wurde, wird eine herunterladbare URL erstellt und sie wird an den unsichtbaren "a"-Link angehängt und dann darauf geklickt.

Hier müssen wir einige Dinge auf der Serverseite sorgfältig einstellen. Ich habe einige Header in Python Django HttpResponse gesetzt. Sie müssen sie entsprechend einstellen, wenn Sie andere Programmiersprachen verwenden.

# In Python Django Code
response = HttpResponse(file_content, content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")

Da ich hier eine xls(Excel)-Datei herunterlade, habe ich den contentType entsprechend angepasst. Sie müssen ihn entsprechend Ihrem Dateityp einstellen. Sie können diese Technik verwenden, um alle Arten von Dateien herunterzuladen.

35voto

Robin van Baalen Punkte 3552

Welche serverseitige Sprache verwenden Sie? In meiner App kann ich leicht eine Datei von einem AJAX-Aufruf herunterladen, indem ich die richtigen Header in der PHP-Antwort einstelle:

Header serverseitig einstellen

header("HTTP/1.1 200 OK");
header("Pragma: public");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");

// Der optionale zweite 'replace'-Parameter gibt an, ob der Header
// einen zuvor ähnlichen Header ersetzen oder einen zweiten Header
// des gleichen Typs hinzufügen soll. Standardmäßig ersetzt es, aber 
// wenn Sie FALSE als zweites Argument übergeben, können Sie mehrere
// Header desselben Typs erzwingen.
header("Cache-Control: private", false);

header("Content-type: " . $mimeType);

// $strFileName ist natürlich der Dateiname der heruntergeladenen Datei.
// Dies muss nicht der gleiche Name wie die tatsächliche Datei sein.
header("Content-Disposition: attachment; filename=\"{$strFileName}\""); 

header("Content-Transfer-Encoding: binary");
header("Content-Length: " . mb_strlen($strFile));

// $strFile ist eine binäre Darstellung der heruntergeladenen Datei.
echo $strFile;

Dies wird tatsächlich den Browser auf diese Download-Seite 'umleiten', aber wie @ahren bereits in seinem Kommentar sagte, wird er nicht von der aktuellen Seite navigieren.

Es geht darum, die richtigen Header einzustellen, also bin ich sicher, dass Sie eine geeignete Lösung für die serverseitige Sprache finden werden, die Sie verwenden, wenn es nicht PHP ist.

Behandlung der Antwort auf der Clientseite

Angenommen, Sie wissen bereits, wie man einen AJAX-Aufruf macht. Auf der Clientseite führen Sie dann eine AJAX-Anfrage an den Server aus. Der Server generiert dann einen Link, von dem aus diese Datei heruntergeladen werden kann, z.B. die 'weiterleitende' URL, auf die Sie zeigen möchten. Zum Beispiel antwortet der Server mit:

{
    status: 1, // ok
    // einzigartiges einmaliges Download-Token, nicht erforderlich, natürlich
    message: 'http://ihrewebsite.com/getdownload/ska08912dsa'
}

Beim Verarbeiten der Antwort fügen Sie ein iframe in Ihren Körper ein und setzen den SRC des iframe auf die URL, die Sie gerade erhalten haben, wie folgt (mit jQuery für die Vereinfachung dieses Beispiels):

$("body").append("");

Wenn Sie die Header richtig gesetzt haben, wird das iframe einen Download-Dialog erzwingen, ohne den Browser von der aktuellen Seite zu navigieren.

Anmerkung

Zusätzliche Ergänzung in Bezug auf Ihre Frage; Ich denke, es ist am besten, immer JSON zurückzugeben, wenn Sie mit AJAX-Technologie nach Daten fragen. Nachdem Sie die JSON-Antwort erhalten haben, können Sie dann auf der Clientseite entscheiden, was damit zu tun ist. Möglicherweise möchten Sie beispielsweise später, dass der Benutzer auf einen Download-Link zur URL klickt, anstatt den Download direkt zu erzwingen. In Ihrer aktuellen Konfiguration müssten Sie sowohl auf Client- als auch auf Serverseite aktualisieren, um dies zu tun.

33voto

Mayur Padshala Punkte 1927

Hier ist, wie ich es zum Laufen gebracht habe https://stackoverflow.com/a/27563953/2845977

$.ajax({
  url: '',
  success: function(data) {
    var blob=new Blob([data]);
    var link=document.createElement('a');
    link.href=window.URL.createObjectURL(blob);
    link.download="";
    link.click();
  }
});

Aktualisierte Antwort mit download.js

$.ajax({
  url: '',
  success: download.bind(true, "", "")
});

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X