Gestisci il download di file da ajax post


393

Ho un'app javascript che invia richieste POST ajax a un determinato URL. La risposta potrebbe essere una stringa JSON o potrebbe essere un file (come allegato). Riesco a rilevare facilmente Content-Type e Content-Disposition nella mia chiamata ajax, ma una volta rilevato che la risposta contiene un file, come posso offrire al client di scaricarlo? Ho letto un numero di thread simili qui, ma nessuno di essi fornisce la risposta che sto cercando.

Per favore, per favore, non pubblicare risposte che suggeriscono che non dovrei usare Ajax per questo o che dovrei reindirizzare il browser, perché nessuna di queste è un'opzione. Anche l'utilizzo di un semplice modulo HTML non è un'opzione. Ciò di cui ho bisogno è mostrare una finestra di download al client. Questo può essere fatto e come?


Per coloro che hanno letto questo articolo, leggere questo post: stackoverflow.com/questions/20830309/...
Sobhan

Ho rimosso la tua soluzione dalla domanda. Puoi inviarlo come post di risposta di seguito, ma non appartiene al post della domanda.
Martijn Pieters

Risposte:


111

Crea un modulo, usa il metodo POST, invia il modulo - non è necessario un iframe. Quando la pagina del server risponde alla richiesta, scrivi un'intestazione di risposta per il tipo mime del file e presenterà una finestra di download - l'ho fatto diverse volte.

Desideri il tipo di contenuto di applicazione / download: cerca solo come fornire un download per qualsiasi lingua tu stia utilizzando.


35
Come indicato nella domanda: "Anche l'uso di un semplice modulo HTML non è un'opzione".
Pavle Predic,

13
No, perché l'utilizzo di un normale POST consente di navigare nel browser all'URL POST. Non voglio allontanarmi dalla pagina. Voglio eseguire la richiesta in background, elaborare la risposta e presentarla al cliente.
Pavle Predic,

6
Se il server rispedisce le intestazioni come ha fatto l'altra risposta, si apre in una nuova finestra - l'ho già fatto prima.

1
@PavlePredic sei riuscito a capire come gestire entrambi gli scenari di risposta, ovvero la risposta testuale JSON o la risposta al download di file?
Utente Web

9
La risposta non è chiara e la soluzione proposta non funziona.
stack247

531

Non arrenderti così rapidamente, perché questo può essere fatto (nei browser moderni) usando parti del FileAPI:

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;
        if (typeof File === 'function') {
            try {
                blob = new File([this.response], filename, { type: type });
            } catch (e) { /* Edge */ }
        }
        if (typeof blob === 'undefined') {
            blob = 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.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); // cleanup
        }
    }
};
xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
xhr.send($.param(params));

Ecco la vecchia versione che utilizza jQuery.ajax. Potrebbe alterare i dati binari quando la risposta viene convertita in una stringa di alcuni set di caratteri.

$.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.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); // cleanup
        }
    }
});

1
Molte grazie ! Ho dovuto aggiungere "Content-disposition" a "Access-Control-Expose-Headers" e "Access-Control-Allow-Headers" nella mia risposta HTTP per farlo funzionare, però.
JulienD,

1
Non funziona quando il file è più grande di 500 mb, forse dovremmo usare un altro API?
Hirra,

Che ne dici di rimuovere un elemento dal DOM nella parte di pulizia (e non solo l'URL)? document.body.removeChild(a);
Scoregraphic

@hirra usa responceType "blob" invece di "arraybuffer" e riscrive la funzione di callback onload in quel modo, quel var varb è this.response (var blob = this.response;) quindivar blob =this.responce; /** if (typeof File === 'function') { try { blob = new File([this.response], filename, { type: type }); } catch (e) { /* Edge */ } } if (typeof blob === 'undefined') { blob = new Blob([this.response], { type: type }); } */
Chris Tobba,

1
Questa è la soluzione perfetta Solo un piccolo cambiamento. In Typescript avevo bisogno window.location.href = downloadUrlinvece diwindow.location = downloadUrl
michal.jakubeczy il

39

Ho affrontato lo stesso problema e risolto con successo. Il mio caso d'uso è questo.

" Pubblica i dati JSON sul server e ricevi un file Excel. Quel file Excel viene creato dal server e restituito come risposta al client. Scarica quella risposta come file con nome personalizzato nel browser "

$("#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));
});

Lo snippet sopra sta semplicemente facendo ciò che segue

  • Pubblicazione di un array come JSON sul server mediante XMLHttpRequest.
  • Dopo aver recuperato il contenuto come BLOB (binario), stiamo creando un URL scaricabile e lo alleghiamo a un collegamento "a" invisibile, quindi facciamo clic su di esso.

Qui dobbiamo impostare con cura alcune cose sul lato server. Ho impostato alcune intestazioni in Python Django HttpResponse. È necessario impostarli di conseguenza se si utilizzano altri linguaggi di programmazione.

# In python django code
response = HttpResponse(file_content, content_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")

Da quando ho scaricato xls (Excel) qui, ho modificato contentType su uno sopra. È necessario impostarlo in base al tipo di file. È possibile utilizzare questa tecnica per scaricare qualsiasi tipo di file.


33

Quale lingua lato server stai usando? Nella mia app posso facilmente scaricare un file da una chiamata AJAX impostando le intestazioni corrette nella risposta di PHP:

Impostazione delle intestazioni lato server

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

// The optional second 'replace' parameter indicates whether the header
// should replace a previous similar header, or add a second header of
// the same type. By default it will replace, but if you pass in FALSE
// as the second argument you can force multiple headers of the same type.
header("Cache-Control: private", false);

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

// $strFileName is, of course, the filename of the file being downloaded. 
// This won't have to be the same name as the actual file.
header("Content-Disposition: attachment; filename=\"{$strFileName}\""); 

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

// $strFile is a binary representation of the file that is being downloaded.
echo $strFile;

Questo infatti "reindirizzerà" il browser a questa pagina di download, ma come ha detto @ahren nel suo commento, non si allontanerà dalla pagina corrente.

Si tratta solo di impostare le intestazioni corrette, quindi sono sicuro che troverai una soluzione adatta per la lingua lato server che stai utilizzando se non è PHP.

Gestire il lato client di risposta

Supponendo che tu sappia già come effettuare una chiamata AJAX, sul lato client esegui una richiesta AJAX al server. Il server genera quindi un collegamento da cui è possibile scaricare questo file, ad esempio l'URL "forward" su cui si desidera puntare. Ad esempio, il server risponde con:

{
    status: 1, // ok
    // unique one-time download token, not required of course
    message: 'http://yourwebsite.com/getdownload/ska08912dsa'
}

Durante l'elaborazione della risposta, si inietta un iframenel proprio corpo e si imposta l' iframeSRC sull'URL appena ricevuto in questo modo (usando jQuery per la facilità di questo esempio):

$("body").append("<iframe src='" + data.message +
  "' style='display: none;' ></iframe>");

Se hai impostato le intestazioni corrette come mostrato sopra, l'iframe forzerà una finestra di download senza spostare il browser lontano dalla pagina corrente.

Nota

Aggiunta extra in relazione alla tua domanda; Penso che sia meglio restituire sempre JSON quando si richiedono cose con la tecnologia AJAX. Dopo aver ricevuto la risposta JSON, puoi decidere sul lato client cosa farne. Forse, ad esempio, in seguito si desidera che l'utente faccia clic su un collegamento di download all'URL invece di forzare il download direttamente, nella configurazione corrente è necessario aggiornare sia il client che il lato server per farlo.


24

Per coloro che cercano una soluzione dal punto di vista angolare, questo ha funzionato per me:

$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 = "Filename";
  link.click();
});

Questo ha aiutato, ma devo preservare il nome file originale. Vedo il nome del file nelle intestazioni della risposta in "Disposizione contenuto", ma non riesco a trovare quel valore nell'oggetto risposta nel codice. L'impostazione link.download = ""genera un nome file guid casuale e link.download = nullgenera un file denominato "null".
Marie

@Marie, puoi registrare il nome del file al momento del caricamento utilizzando la proprietà INPUTdell'elemento HTMLInputElement.files. Vedi I documenti MDN sull'input del file per maggiori dettagli.
Tim Hettler,

Dimensioni Blob è limitata: stackoverflow.com/questions/28307789/...
user0800

22

Ecco come ho ottenuto questo lavoro https://stackoverflow.com/a/27563953/2845977

$.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();
  }
});

Risposta aggiornata utilizzando download.js

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


Grazie per questo, l'ho usato solo oggi. Fantastico
Ryan Wilson,

Ciao, devo avere jQuery 3.0> perché funzioni?
gbade_

Ricevo anche un pdf vuoto con entrambi gli esempi che hai fornito. Sto cercando di scaricarlo per scaricare un file pdf.
gbade_

@gbade_ No, non hai bisogno di alcuna versione specifica di jQuery. Dovrebbe funzionare bene con tutte le versioni. Hai verificato se il PDF che stai scaricando ha le intestazioni CORS corrette? Eventuali errori sulla console potrebbero aiutare a eseguire il debug
Mayur Padshala,

Questo ha funzionato per me usando download.js:success: function (response, status, request) { download(response, "filename.txt", "application/text"); }
Sga

12

Vedo che hai già trovato una soluzione, tuttavia volevo solo aggiungere alcune informazioni che potrebbero aiutare qualcuno che cerca di ottenere la stessa cosa con grandi richieste POST.

Ho avuto lo stesso problema un paio di settimane fa, in effetti non è possibile ottenere un download "pulito" tramite AJAX, il gruppo Filament ha creato un plug-in jQuery che funziona esattamente come l'hai già scoperto, si chiama jQuery File Scarica tuttavia c'è un aspetto negativo di questa tecnica.

Se stai inviando grandi richieste tramite AJAX (diciamo file + 1 MB) avrà un impatto negativo sulla reattività. Nelle connessioni Internet lente dovrai aspettare molto fino della richiesta e attendere il download del file. Non è come un istante "click" => "popup" => "download start". È più simile a "click" => "aspetta fino all'invio dei dati" => "wait for response" => "download start" che fa apparire il file il doppio della sua dimensione perché dovrai aspettare che la richiesta sia inviata tramite AJAX e ripristinarlo come file scaricabile.

Se lavori con file di piccole dimensioni <1 MB non noterai questo. Ma come ho scoperto nella mia app, per file di dimensioni maggiori è quasi insopportabile.

La mia app consente agli utenti di esportare immagini generate dinamicamente, queste immagini vengono inviate al server tramite richieste POST in formato base64 (è l'unico modo possibile), quindi elaborate e rispedite agli utenti sotto forma di .png, file .jpg, base64 le stringhe per immagini + 1 MB sono enormi, questo costringe gli utenti ad attendere più del necessario per avviare il download del file. Nelle connessioni Internet lente può essere davvero fastidioso.

La mia soluzione per questo era scrivere temporaneamente il file sul server, una volta pronto, generare dinamicamente un collegamento al file sotto forma di un pulsante che cambia tra gli stati "Please wait ..." e "Download" e allo stesso tempo ora, stampa l'immagine base64 in una finestra popup di anteprima in modo che gli utenti possano "fare clic con il tasto destro" e salvarla. Ciò rende più sopportabile tutto il tempo di attesa per gli utenti e velocizza le cose.

Aggiornamento 30 settembre 2014:

Sono passati mesi da quando l'ho pubblicato, finalmente ho trovato un approccio migliore per velocizzare le cose quando si lavora con grandi stringhe base64. Ora memorizzo le stringhe di base64 nel database (usando i campi longtext o longblog), quindi passo il suo ID record attraverso il download di file jQuery, infine sul file di script di download interrogo il database utilizzando questo ID per estrarre la stringa di base64 e passarla attraverso la funzione di download.

Scarica esempio di script:

<?php
// Record ID
$downloadID = (int)$_POST['id'];
// Query Data (this example uses CodeIgniter)
$data       = $CI->MyQueries->GetDownload( $downloadID );
// base64 tags are replaced by [removed], so we strip them out
$base64     = base64_decode( preg_replace('#\[removed\]#', '', $data[0]->image) );
// This example is for base64 images
$imgsize    = getimagesize( $base64 );
// Set content headers
header('Content-Disposition: attachment; filename="my-file.png"');
header('Content-type: '.$imgsize['mime']);
// Force download
echo $base64;
?>

So che è molto al di là di ciò che l'OP ha chiesto, tuttavia ho ritenuto opportuno aggiornare la mia risposta con i miei risultati. Durante la ricerca di soluzioni al mio problema, ho letto molti thread "Scarica da dati POST AJAX" che non mi hanno dato la risposta che stavo cercando, spero che queste informazioni aiutino qualcuno che cerca di ottenere qualcosa del genere.


L' jQuery File Downloadunico reindirizzami all'URL. Io lo chiamo così: jQuery.download("api/ide/download-this-file.php", {filePath: path2Down}, "POST");.
Casper,

5

Voglio sottolineare alcune difficoltà che sorgono quando si utilizza la tecnica nella risposta accettata, ovvero utilizzando un modulo post:

  1. Non è possibile impostare le intestazioni sulla richiesta. Se lo schema di autenticazione prevede intestazioni, un token Json-Web passato nell'intestazione Autorizzazione, dovrai trovare un altro modo per inviarlo, ad esempio come parametro di query.

  2. Non puoi davvero dire quando la richiesta è terminata. Bene, puoi usare un cookie che viene impostato sulla risposta, come fatto da jquery.fileDownload , ma è FAR da perfetto. Non funzionerà per richieste simultanee e si interromperà se non arriva mai una risposta.

  3. Se il server risponde con un errore, l'utente verrà reindirizzato alla pagina di errore.

  4. È possibile utilizzare solo i tipi di contenuto supportati da un modulo . Ciò significa che non è possibile utilizzare JSON.

Ho finito con il metodo di salvataggio del file su S3 e l'invio di un URL pre-firmato per ottenere il file.


5

Per coloro che cercano un approccio più moderno, è possibile utilizzare il fetch API. L'esempio seguente mostra come scaricare un file di foglio di calcolo. È facilmente eseguibile con il seguente codice.

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();
})

Credo che questo approccio sia molto più facile da capire rispetto ad altre XMLHttpRequestsoluzioni. Inoltre, ha una sintassi simile jQueryall'approccio, senza la necessità di aggiungere ulteriori librerie.

Naturalmente, consiglierei di verificare su quale browser stai sviluppando, poiché questo nuovo approccio non funzionerà su IE. Puoi trovare l'elenco completo di compatibilità dei browser sul seguente [link] [1].

Importante : in questo esempio sto inviando una richiesta JSON a un server in ascolto sul dato url. Questo urldeve essere impostato, sul mio esempio presumo che tu conosca questa parte. Inoltre, considera le intestazioni necessarie affinché la tua richiesta funzioni. Dal momento che sto inviando un JSON, devo aggiungere l' Content-Typeintestazione e impostarlo su application/json; charset=utf-8, in modo da comunicare al server il tipo di richiesta che riceverà.


1
Eccezionale! Per aprirlo in una nuova scheda anziché in un popup di download: usa `` `const window = open (downloadUrl," _blank "); if (window! == null) window.focus (); ``
andy

C'è un modo per farlo per più insiemi di dati? Esempio, tornare indietro {fileOne: data, fileTwo: data, fileThree: data} dalla chiamata API e generare tre file scaricati contemporaneamente? Grazie!
il0v3d0g

Hmmm, non l'ho provato. Ma puoi sempre comprimere le immagini in un file zip e scaricarlo. Controllerò se è possibile.
Alain Cruz,

4

Ecco la mia soluzione usando un modulo nascosto temporaneo.

//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();

Nota che uso in modo massiccio JQuery ma puoi fare lo stesso con JS nativo.


3

Come altri hanno già affermato, è possibile creare e inviare un modulo da scaricare tramite una richiesta POST. Tuttavia, non è necessario farlo manualmente.

Una libreria davvero semplice per fare esattamente questo è jquery.redirect . Fornisce un'API simile al jQuery.postmetodo standard :

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

3

Questa è una domanda di 3 anni ma oggi ho avuto lo stesso problema. Ho cercato la tua soluzione modificata, ma penso che possa sacrificare le prestazioni perché deve fare una doppia richiesta. Quindi, se qualcuno ha bisogno di un'altra soluzione che non implica chiamare il servizio due volte, questo è il modo in cui l'ho fatto:

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

Questo modulo viene usato solo per chiamare il servizio ed evitare di usare window.location (). Dopodiché devi semplicemente inviare un modulo da jquery per chiamare il servizio e ottenere il file. È piuttosto semplice ma in questo modo è possibile effettuare un download utilizzando un POST . Ora che questo potrebbe essere più semplice se il servizio che stai chiamando è un GET , ma non è il mio caso.


1
Questo non è un post di
Ajax in

è solo una soluzione al problema precedente, tuttavia non per le chiamate Ajax.
Nomi Ali,

1

Ho usato questo FileSaver.js . Nel mio caso con i file CSV, l'ho fatto (nel coffescript):

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

Penso che per il caso più complicato, i dati debbano essere elaborati correttamente. Sotto il cofano FileSaver.js implementa lo stesso approccio della risposta di Jonathan Amend .


1
..ma puoi scaricare file di solito in iOS?
Alex Marshall,


1

Per ottenere la risposta di Jonathan Amends al lavoro in Edge, ho apportato le seguenti modifiche:

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

a questo

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

Preferirei averlo pubblicato come commento, ma non ho abbastanza reputazione per quello


0

Ecco la mia soluzione, raccolta da diverse fonti: Implementazione lato server:

    String contentType = MediaType.APPLICATION_OCTET_STREAM_VALUE;
    // Set headers
    response.setHeader("content-disposition", "attachment; filename =" + fileName);
    response.setContentType(contentType);
    // Copy file to output stream
    ServletOutputStream servletOutputStream = response.getOutputStream();
    try (InputStream inputStream = new FileInputStream(file)) {
        IOUtils.copy(inputStream, servletOutputStream);
    } finally {
        servletOutputStream.flush();
        Utils.closeQuitely(servletOutputStream);
        fileToDownload = null;
    }

Implementazione lato client (usando jquery):

$.ajax({
type: 'POST',
contentType: 'application/json',
    url: <download file url>,
    data: JSON.stringify(postObject),
    error: function(XMLHttpRequest, textStatus, errorThrown) {
        alert(errorThrown);
    },
    success: function(message, textStatus, response) {
       var header = response.getResponseHeader('Content-Disposition');
       var fileName = header.split("=")[1];
       var blob = new Blob([message]);
       var link = document.createElement('a');
       link.href = window.URL.createObjectURL(blob);
       link.download = fileName;
       link.click();
    }
});   

0

esiste un'altra soluzione per scaricare una pagina Web in Ajax. Ma mi riferisco a una pagina che deve prima essere elaborata e quindi scaricata.

Per prima cosa devi separare l'elaborazione della pagina dal download dei risultati.

1) Nella chiamata ajax vengono effettuati solo i calcoli della pagina.

$ .post ("CalculusPage.php", {calculusFunction: true, ID: 29, data1: "a", data2: "b"},

       funzione (dati, stato) 
       {
            if (status == "successo") 
            {
                / * 2) Nella risposta viene scaricata la pagina che utilizza i calcoli precedenti. Ad esempio, questa può essere una pagina che stampa i risultati di una tabella calcolata nella chiamata ajax. * /
                window.location.href = DownloadPage.php + "? ID =" + 29;
            }               
       }
);

// Ad esempio: in CalculusPage.php

    if (! empty ($ _ POST ["calculusFunction"])) 
    {
        $ ID = $ _POST ["ID"];

        $ query = "INSERT INTO ExamplePage (data1, data2) VALUES ('". $ _ POST ["data1"]. "', '". $ _ POST ["data2"]. "') WHERE id =". $ ID;
        ...
    }

// Ad esempio: in DownloadPage.php

    $ ID = $ _GET ["ID"];

    $ sede = "SELEZIONA * DA ExamplePage DOVE id =". $ ID;
    ...

    $ Nomefile = "Export_Data.xls";
    header ("Content-Type: application / vnd.ms-excel");
    header ("Content-Disposition: inline; nomefile = $ nomefile");

    ...

Spero che questa soluzione possa essere utile per molti, come è stata per me.


0

Se la risposta è un buffer di array , provare questo nell'evento onsuccess in Ajax:

 if (event.data instanceof ArrayBuffer) {
          var binary = '';
          var bytes = new Uint8Array(event.data);
          for (var i = 0; i < bytes.byteLength; i++) {
              binary += String.fromCharCode(bytes[i])
          }
          $("#some_id").append("<li><img src=\"data:image/png;base64," + window.btoa(binary) + "\"/></span></li>");
          return;
      }
  • dove event.data è la risposta ricevuta nella funzione di successo dell'evento xhr.

0

Di seguito è la mia soluzione per il download di più file a seconda di un elenco che comprende alcuni ID e la ricerca nel database, i file saranno determinati e pronti per il download - se presenti. Sto chiamando l'azione C # MVC per ogni file usando Ajax.

E sì, come altri hanno detto, è possibile farlo in jQuery Ajax. L'ho fatto con successo con l'Ajax e invio sempre la risposta 200.

Quindi, questa è la chiave:

  success: function (data, textStatus, xhr) {

E questo è il mio codice:

var i = 0;
var max = 0;
function DownloadMultipleFiles() {
            if ($(".dataTables_scrollBody>tr.selected").length > 0) {
                var list = [];
                showPreloader();
                $(".dataTables_scrollBody>tr.selected").each(function (e) {
                    var element = $(this);
                    var orderid = element.data("orderid");
                    var iscustom = element.data("iscustom");
                    var orderlineid = element.data("orderlineid");
                    var folderPath = "";
                    var fileName = "";

                    list.push({ orderId: orderid, isCustomOrderLine: iscustom, orderLineId: orderlineid, folderPath: folderPath, fileName: fileName });
                });
                i = 0;
                max = list.length;
                DownloadFile(list);
            }
        }

Quindi chiamando:

function DownloadFile(list) {
        $.ajax({
            url: '@Url.Action("OpenFile","OrderLines")',
            type: "post",
            data: list[i],
            xhrFields: {
                responseType: 'blob'
            },
            beforeSend: function (xhr) {
                xhr.setRequestHeader("RequestVerificationToken",
                    $('input:hidden[name="__RequestVerificationToken"]').val());

            },
            success: function (data, textStatus, 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 a = document.createElement('a');
                    var url = window.URL.createObjectURL(data);
                    a.href = url;
                    a.download = filename;
                    document.body.append(a);
                    a.click();
                    a.remove();
                    window.URL.revokeObjectURL(url);
                }
                else {
                    getErrorToastMessage("Production file for order line " + list[i].orderLineId + " does not exist");
                }
                i = i + 1;
                if (i < max) {
                    DownloadFile(list);
                }
            },
            error: function (XMLHttpRequest, textStatus, errorThrown) {

            },
            complete: function () {
                if(i===max)
                hidePreloader();
            }
        });
    }

C # MVC:

 [HttpPost]
 [ValidateAntiForgeryToken]
public IActionResult OpenFile(OrderLineSimpleModel model)
        {
            byte[] file = null;

            try
            {
                if (model != null)
                {
                    //code for getting file from api - part is missing here as not important for this example
                    file = apiHandler.Get<byte[]>(downloadApiUrl, token);

                    var contentDispositionHeader = new System.Net.Mime.ContentDisposition
                    {
                        Inline = true,
                        FileName = fileName
                    };
                    //    Response.Headers.Add("Content-Disposition", contentDispositionHeader.ToString() + "; attachment");
                    Response.Headers.Add("Content-Type", "application/pdf");
                    Response.Headers.Add("Content-Disposition", "attachment; filename=" + fileName);
                    Response.Headers.Add("Content-Transfer-Encoding", "binary");
                    Response.Headers.Add("Content-Length", file.Length.ToString());

                }
            }
            catch (Exception ex)
            {
                this.logger.LogError(ex, "Error getting pdf", null);
                return Ok();
            }

            return File(file, System.Net.Mime.MediaTypeNames.Application.Pdf);
        }

Finché si restituisce la risposta 200, il successo in Ajax può funzionare con esso, è possibile verificare se il file esiste effettivamente o meno poiché la riga seguente in questo caso sarebbe falsa e si può informare l'utente di ciò:

 if (disposition && disposition.indexOf('attachment') !== -1) {

0

Avevo bisogno di una soluzione simile a quella di @ alain-cruz, ma in nuxt / vue con download multipli. So che i browser bloccano più download di file e ho anche un'API che restituisce una serie di dati in formato CSV. Inizialmente avrei usato JSZip ma avevo bisogno del supporto IE, quindi ecco la mia soluzione. Se qualcuno mi può aiutare a migliorare questo sarebbe fantastico, ma finora funziona per me.

Resi API:

data : {
  body: {
    fileOne: ""col1", "col2", "datarow1.1", "datarow1.2"...so on",
    fileTwo: ""col1", "col2"..."
  }
}

page.vue:

<template>
  <b-link @click.prevent="handleFileExport">Export<b-link>
</template>

export default = {
   data() {
     return {
       fileNames: ['fileOne', 'fileTwo'],
     }
   },
  computed: {
    ...mapState({
       fileOne: (state) => state.exportFile.fileOne,
       fileTwo: (state) => state.exportFile.fileTwo,
    }),
  },
  method: {
    handleExport() {
      //exportFileAction in store/exportFile needs to return promise
      this.$store.dispatch('exportFile/exportFileAction', paramsToSend)
        .then(async (response) => {
           const downloadPrep = this.fileNames.map(async (fileName) => {
           // using lodash to get computed data by the file name
           const currentData = await _.get(this, `${fileName}`);
           const currentFileName = fileName;
           return { currentData, currentFileName };
         });
         const response = await Promise.all(downloadPrep);
         return response;
       })
       .then(async (data) => {
         data.forEach(({ currentData, currentFileName }) => {
           this.forceFileDownload(currentData, currentFileName);
         });
       })
       .catch(console.error);
    },
    forceFileDownload(data, fileName) {
     const url = window.URL
         .createObjectURL(new Blob([data], { type: 'text/csv;charset=utf-8;' }));
     const link = document.createElement('a');
     link.href = url;
     link.setAttribute('download', `${fileName}.csv`);
     document.body.appendChild(link);
     link.click();
   },
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.