javascript - example - Handle Datei-Download von Ajax Post




jquery ajax timeout (10)

Dies ist eine 3 Jahre alte Frage, aber ich hatte heute das gleiche Problem. Ich habe deine bearbeitete Lösung angeschaut, aber ich denke, dass sie die Leistung opfern kann, weil sie eine doppelte Anfrage stellen muss. Wenn also jemand eine andere Lösung benötigt, die nicht bedeutet, den Dienst zweimal anzurufen, dann habe ich das so gemacht:

<form id="export-csv-form" method="POST" action="/the/path/to/file">
    <input type="hidden" name="anyValueToPassTheServer" value="">
</form>

Dieses Formular wird nur verwendet, um den Dienst aufzurufen und die Verwendung von window.location () zu vermeiden. Danach müssen Sie lediglich ein Formular von jquery senden, um den Dienst aufzurufen und die Datei abzurufen. Es ist ziemlich einfach, aber auf diese Weise können Sie einen Download mit einem POST machen . Ich jetzt, dass dies einfacher sein könnte, wenn der Service, den Sie anrufen, ein GET ist , aber das ist nicht mein Fall.

Ich habe eine Javascript-App, die ajax POST-Anfragen an eine bestimmte URL sendet. Die Antwort könnte eine JSON-Zeichenfolge oder eine Datei (als Anhang) sein. Ich kann Content-Type und Content-Disposition in meinem Ajax-Aufruf leicht erkennen, aber sobald ich feststelle, dass die Antwort eine Datei enthält, wie biete ich dem Client an, sie herunterzuladen? Ich habe eine Reihe von ähnlichen Themen hier gelesen, aber keiner von ihnen bietet die Antwort, die ich suche.

Bitte, bitte, bitte posten Sie keine Antworten, die darauf hindeuten, dass ich keinen Ajax für das oder den Browser verwenden sollte, da dies keine Option ist. Die Verwendung eines einfachen HTML-Formulars ist ebenfalls keine Option. Was ich brauche, ist, einen Download-Dialog zum Client zu zeigen. Kann das gemacht werden und wie?

BEARBEITEN:

Offensichtlich kann dies nicht getan werden, aber es gibt eine einfache Problemumgehung, wie von der angenommenen Antwort vorgeschlagen. Für jeden, der in Zukunft auf dieses Problem stößt, habe ich es so gelöst:

$.ajax({
    type: "POST",
    url: url,
    data: params,
    success: function(response, status, request) {
        var disp = request.getResponseHeader('Content-Disposition');
        if (disp && disp.search('attachment') != -1) {
            var form = $('<form method="POST" action="' + url + '">');
            $.each(params, function(k, v) {
                form.append($('<input type="hidden" name="' + k +
                        '" value="' + v + '">'));
            });
            $('body').append(form);
            form.submit();
        }
    }
});

Also generiere einfach ein HTML-Formular mit den gleichen Parametern, die in der AJAX-Anfrage verwendet wurden, und sende es ab.


Erstellen Sie ein Formular, verwenden Sie die POST-Methode, senden Sie das Formular - ein Iframe ist nicht erforderlich. Wenn die Serverseite auf die Anfrage reagiert, schreibe einen Antwortheader für den MIME-Typ der Datei und es wird ein Download-Dialog angezeigt - ich habe dies mehrmals getan.

Sie möchten den Inhaltstyp der Anwendung / des Downloads - suchen Sie einfach nach dem Download für die von Ihnen verwendete Sprache.


Gib nicht so schnell auf, denn dies kann (in modernen Browsern) mit Teilen der FileAPI geschehen:

Bearbeiten 2017-09-28: Aktualisiert, um den Dateikonstruktor zu verwenden, wenn er verfügbar ist, damit er in Safari> = 10.1 funktioniert.

Bearbeiten 2015-10-16: jQuery ajax ist nicht in der Lage, binäre Antworten richtig zu verarbeiten (responseType kann nicht gesetzt werden). Daher ist es besser, einen einfachen XMLHttpRequest-Aufruf zu verwenden.

var xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
xhr.responseType = 'arraybuffer';
xhr.onload = function () {
    if (this.status === 200) {
        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 = typeof File === 'function'
            ? new File([this.response], filename, { type: type })
            : new Blob([this.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
        }
    }
};
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send($.param(params));

Hier ist die alte Version mit jQuery.ajax. Es kann Binärdaten beschädigen, wenn die Antwort in eine Zeichenkette eines Zeichensatzes konvertiert wird.

$.ajax({
    type: "POST",
    url: url,
    data: params,
    success: function(response, status, xhr) {
        // check for a filename
        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
        }
    }
});

Hier ist meine Lösung mit einem temporären versteckten Formular.

//Create an hidden form
var form = $('<form>', {'method': 'POST', 'action': this.href}).hide();

//Add params
var params = { ...your params... };
$.each(params, function (k, v) {
    form.append($('<input>', {'type': 'hidden', 'name': k, 'value': v}));
});

//Make it part of the document and submit
$('body').append(form);
form.submit();

//Clean up
form.remove();

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


Ich habe diese FileSaver.js . In meinem Fall mit CSV-Dateien habe ich das (in Coffescript) gemacht:

  $.ajax
    url: "url-to-server"
    data: "data-to-send"
    success: (csvData)->
      blob = new Blob([csvData], { type: 'text/csv' })
      saveAs(blob, "filename.csv")

Ich denke, für den kompliziertesten Fall müssen die Daten ordnungsgemäß verarbeitet werden. Unter der Haube implementieren FileSaver.js den gleichen Ansatz der Antwort von .


Ich möchte auf einige Schwierigkeiten hinweisen, die sich ergeben, wenn man die Technik in der akzeptierten Antwort verwendet, dh mit einem Formularpost:

  1. Sie können für die Anforderung keine Header festlegen. Wenn Ihr Authentifizierungsschema Header enthält, wird ein Json-Web-Token im Authorization-Header übergeben, Sie müssen andere Wege 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 auf die Antwort gesetzt wird, wie es von jquery.fileDownload erledigt wird, aber es ist FAR von perfekt. Es funktioniert nicht für gleichzeitige Anfragen und es bricht, 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 von einem form unterstützten Inhaltstypen verwenden. Das bedeutet, dass Sie JSON nicht verwenden können.

Am Ende habe ich die Methode zum Speichern der Datei in S3 und das Senden einer vorzeichneten URL verwendet, um die Datei zu erhalten.


Ich stand vor demselben Problem und habe es erfolgreich gelöst. Mein Anwendungsfall ist das.

" Veröffentlichen Sie JSON-Daten auf dem Server und erhalten Sie eine Excel-Datei. Diese Excel-Datei wird vom Server erstellt und als Antwort an den Client zurückgegeben. Laden Sie diese Antwort als Datei mit benutzerdefiniertem Namen im Browser herunter. "

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

// Data to post
data = {
    ids: [1, 2, 3, 4, 5]
};

// Use XMLHttpRequest instead of Jquery $ajax
xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
    var a;
    if (xhttp.readyState === 4 && xhttp.status === 200) {
        // Trick for making downloadable link
        a = document.createElement('a');
        a.href = window.URL.createObjectURL(xhttp.response);
        // Give filename you wish to download
        a.download = "test-file.xls";
        a.style.display = 'none';
        document.body.appendChild(a);
        a.click();
    }
};
// Post data to URL which handles post request
xhttp.open("POST", excelDownloadUrl);
xhttp.setRequestHeader("Content-Type", "application/json");
// You should set responseType as blob for binary responses
xhttp.responseType = 'blob';
xhttp.send(JSON.stringify(data));
});

Das obige Snippet macht nur folgendes

  • Ein Array als JSON über XMLHttpRequest an den Server senden.
  • Nachdem wir Inhalt als Blob (binär) abgerufen haben, erstellen wir eine herunterladbare URL und hängen sie an den unsichtbaren "a" -Link an und klicken dann darauf.

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 xls (excel) herunterlade, habe ich contentType auf über eins eingestellt. Sie müssen es entsprechend Ihrem Dateityp einstellen. Sie können diese Technik verwenden, um jede Art von Dateien herunterzuladen.


Um answer auf die Arbeit in Edge zu bekommen, habe ich folgende Änderungen vorgenommen:

var blob = typeof File === 'function'
    ? new File([this.response], filename, { type: type })
    : new Blob([this.response], { type: type });

dazu

var f = typeof File+"";
var blob = f === 'function' && Modernizr.fileapi
    ? new File([this.response], filename, { type: type })
    : new Blob([this.response], { type: type });

Ich hätte das lieber als Kommentar gepostet, aber dafür habe ich nicht genug Reputation


Wie von anderen angegeben, können Sie ein Formular erstellen und senden, um es über eine POST-Anfrage herunterzuladen. Sie müssen dies jedoch nicht manuell tun.

Eine wirklich einfache Bibliothek, um genau das zu tun, ist jquery.redirect . Es bietet eine API, die der Standardmethode jQuery.post :

$.redirect(url, [values, [method, [target]]])





ajax