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?

29voto

Tim Hettler Punkte 1236

Für diejenigen, die eine Lösung aus einer Angular-Perspektive suchen, hat dies bei mir funktioniert:

$http.post(
  'url',
  {},
  {responseType: 'arraybuffer'}
).then(function (response) {
  var headers = response.headers();
  var blob = new Blob([response.data],{type:headers['content-type']});
  var link = document.createElement('a');
  link.href = window.URL.createObjectURL(blob);
  link.download = "Dateiname";
  link.click();
});

19voto

Alain Cruz Punkte 4176

Für diejenigen, die einen moderneren Ansatz suchen, können Sie die fetch API verwenden. Der folgende Code zeigt, wie man eine Tabellendatei herunterlädt.

fetch(url, {
    body: JSON.stringify(data),
    method: 'POST',
    headers: {
        'Content-Type': 'application/json; charset=utf-8'
    },
})
.then(response => response.blob())
.then(response => {
    const blob = new Blob([response], {type: 'application/application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
    const downloadUrl = URL.createObjectURL(blob);
    const a = document.createElement("a");
    a.href = downloadUrl;
    a.download = "file.xlsx";
    document.body.appendChild(a);
    a.click();
})

Ich glaube, dieser Ansatz ist viel einfacher zu verstehen als andere Lösungen mit XMLHttpRequest. Außerdem hat er eine ähnliche Syntax wie der Ansatz mit jQuery, ohne die Notwendigkeit, zusätzliche Bibliotheken hinzuzufügen.

Natürlich empfehle ich zu überprüfen, für welchen Browser Sie entwickeln, da dieser neue Ansatz nicht im IE funktionieren wird. Sie können die vollständige Liste der Browserkompatibilität unter folgendem Link finden.

Wichtig: In diesem Beispiel sende ich eine JSON-Anforderung an einen Server, der auf der angegebenen url hört. Diese url muss angegeben werden, in meinem Beispiel gehe ich davon aus, dass Sie diesen Teil kennen. Berücksichtigen Sie auch die für Ihre Anforderung erforderlichen Header. Da ich ein JSON sende, muss ich den Content-Type-Header hinzufügen und auf application/json; charset=utf-8 setzen, damit der Server weiß, welche Art von Anforderung er erhalten wird.

12voto

José SAYAGO Punkte 798

Ich sehe, dass du bereits eine Lösung gefunden hast, aber ich wollte nur noch einige Informationen hinzufügen, die jemandem helfen könnten, der versucht, dasselbe mit großen POST-Anfragen zu erreichen.

Ich hatte vor ein paar Wochen dasselbe Problem, es ist tatsächlich nicht möglich, einen "sauberen" Download über AJAX zu erreichen. Die Filament Group hat ein jQuery-Plugin erstellt, das genau so funktioniert, wie du es bereits herausgefunden hast, es heißt jQuery File Download, aber es gibt einen Nachteil bei dieser Technik.

Wenn du große Anfragen über AJAX sendest (zum Beispiel Dateien über 1 MB), wird sich die Reaktionsfähigkeit negativ auswirken. Bei langsamen Internetverbindungen musst du lange warten, bis die Anfrage gesendet wird, und auch darauf warten, dass die Datei heruntergeladen wird. Es ist nicht so wie ein sofortiger "Klick" => "Popup" => "Download starten". Es ist eher wie "Klick" => "Warten, bis Daten gesendet werden" => "Auf Antwort warten" => "Download starten", was dazu führt, dass die Datei doppelt so groß aussieht, weil du auf die Übermittlung der Anfrage über AJAX warten und sie als herunterladbare Datei erhalten musst.

Bei kleinen Dateigrößen unter 1 MB wirst du das nicht bemerken. Aber wie ich in meiner eigenen App festgestellt habe, ist es bei größeren Dateigrößen fast unerträglich.

Meine App ermöglicht es Benutzern, dynamisch generierte Bilder zu exportieren. Diese Bilder werden im base64-Format über POST-Anfragen an den Server gesendet (es ist der einzige mögliche Weg), dort verarbeitet und anschließend in Form von .png-, .jpg-Dateien, base64-Strings für Bilder über 1 MB sind riesig. Dies zwingt Benutzer dazu, länger als nötig auf den Beginn des Dateidownloads zu warten. Bei langsamen Internetverbindungen kann das wirklich nervig sein.

Meine Lösung dafür war, die Datei vorübergehend auf dem Server zu speichern. Sobald sie fertig ist, generiere ich dynamisch einen Link zur Datei in Form eines Buttons, der zwischen den Zuständen "Bitte warten..." und "Download" wechselt, und drucke gleichzeitig das base64-Bild in einem Vorschaufenster aus, sodass Benutzer es mit "Rechtsklick" speichern können. Dadurch wird die Wartezeit für Benutzer erträglicher und beschleunigt gleichzeitig die Dinge.

Update 30. Sep 2014:

Seitdem ich das gepostet habe, sind Monate vergangen, endlich habe ich einen besseren Ansatz gefunden, um die Dinge zu beschleunigen, wenn man mit großen base64-Strings arbeitet. Ich speichere die base64-Strings jetzt in der Datenbank (unter Verwendung von longtext- oder longblob-Feldern), dann gebe ich die Datensatz-ID über das jQuery File Download weiter, und schließlich frage ich im Download-Skript-Datei die Datenbank unter Verwendung dieser ID ab, um den base64-String abzurufen und ihn durch die Download-Funktion zu übergeben.

Beispiel für das Download-Skript:

MyQueries->GetDownload( $downloadID );
// base64-Tags werden durch [removed] ersetzt, also entfernen wir sie
$base64     = base64_decode( preg_replace('#\[removed\]#', '', $data[0]->image) );
// Dieses Beispiel gilt für base64-Bilder
$imgsize    = getimagesize( $base64 );
// Inhaltsheader festlegen
header('Content-Disposition: attachment; filename="my-file.png"');
header('Content-type: '.$imgsize['mime']);
// Download erzwingen
echo $base64;
?>

Ich weiß, dass das weit über das hinausgeht, was der OP gefragt hat, aber ich dachte, es wäre gut, meine Antwort mit meinen Erkenntnissen zu aktualisieren. Als ich nach Lösungen für mein Problem gesucht habe, habe ich viele Threads zu "Download von AJAX POST-Daten" gelesen, die mir nicht die Antwort gegeben haben, die ich gesucht habe. Ich hoffe, diese Informationen helfen jemandem, der versucht, so etwas zu erreichen.

8voto

Ludovic Martin Punkte 83

Hier ist meine Lösung unter Verwendung eines temporären versteckten Formulars.

// Erstellen eines versteckten Formulars
var form = $('

Beachten Sie, dass ich stark JQuery verwende, aber Sie können dasselbe mit reinem JS tun.

7voto

tepez Punkte 526

Ich möchte auf einige Schwierigkeiten hinweisen, die bei der Verwendung der Technik im akzeptierten Antwort auftreten, d.h. die Verwendung eines Formulars:

  1. Sie können keine Header für die Anfrage setzen. Wenn Ihr Authentifizierungsschema Header beinhaltet, z.B. ein Json-Web-Token, das im Autorisierungsheader übergeben wird, müssen Sie einen anderen Weg finden, um es zu senden, zum Beispiel als Abfrageparameter.

  2. Sie können nicht wirklich sagen, wann die Anfrage beendet ist. Nun, Sie können ein Cookie verwenden, das in der Antwort gesetzt wird, wie es von jquery.fileDownload gemacht wird, aber es ist bei weitem nicht perfekt. Es funktioniert nicht für parallele Anfragen und bricht ab, wenn eine Antwort nie ankommt.

  3. Wenn der Server mit einem Fehler antwortet, wird der Benutzer zur Fehlerseite weitergeleitet.

  4. Sie können nur die Inhaltsarten verwenden, die von einem Formular unterstützt werden. Das bedeutet, dass Sie kein JSON verwenden können.

Ich habe mich dafür entschieden, die Methode zu verwenden, die Datei auf S3 zu speichern und einen vorab signierten URL zu senden, um die Datei zu erhalten.

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