scarica il file utilizzando una richiesta ajax


95

Voglio inviare una "richiesta di download ajax" quando clicco su un pulsante, quindi ho provato in questo modo:

javascript:

var xhr = new XMLHttpRequest();
xhr.open("GET", "download.php");
xhr.send();

download.php:

<?
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename= file.txt");
header("Content-Transfer-Encoding: binary");    
readfile("file.txt");
?>

ma non funziona come previsto, come posso fare? Grazie in anticipo


2
Questo non funzionerà, vedere [questa domanda] [1]. [1]: stackoverflow.com/questions/8771342/...
olegsv

2
Dolocation.href='download.php';
Musa

Risposte:


92

Aggiornamento del 27 aprile 2015

L'aspetto emergente della scena HTML5 è l' attributo download . È supportato in Firefox e Chrome e presto in IE11. A seconda delle tue esigenze, potresti usarlo al posto di una richiesta AJAX (o utilizzando window.location) purché il file che desideri scaricare sia sulla stessa origine del tuo sito.

È sempre possibile effettuare la richiesta AJAX / window.locationun fallback utilizzando un po 'di JavaScript per verificare se downloadè supportato e, in caso contrario, cambiandolo in call window.location.

Risposta originale

Non è possibile fare in modo che una richiesta AJAX apra il prompt di download poiché è necessario accedere fisicamente al file per richiedere il download. Invece, potresti usare una funzione di successo per navigare su download.php. Questo aprirà la richiesta di download ma non cambierà la pagina corrente.

$.ajax({
    url: 'download.php',
    type: 'POST',
    success: function() {
        window.location = 'download.php';
    }
});

Anche se questo risponde alla domanda, è meglio usare window.locationed evitare completamente la richiesta AJAX.


39
Questo non chiama il collegamento due volte? Sono in una barca simile ... Sto passando molte informazioni sulla sicurezza nelle intestazioni e sono in grado di analizzare l'oggetto file nella funzione di successo, ma non so come attivare un prompt di download.
user1447679

3
Chiama la pagina due volte, quindi se stai interrogando un database in quella pagina, questo significa 2 viaggi nel DB.
mmmmmm

1
@ user1447679 vedi per una soluzione alternativa: stackoverflow.com/questions/38665947/…
John

1
Lascia che ti spieghi come questo mi ha aiutato ... l'esempio avrebbe potuto essere più completo. con "download.php? get_file = true" o qualcosa del genere ... Ho una funzione ajax che fa un po 'di controllo degli errori durante l'invio di un modulo e quindi crea un file csv. Se il controllo degli errori fallisce, deve tornare con il motivo per cui non è riuscito. Se crea il CSV sta dicendo al genitore che "vai avanti e prendi il file". Lo faccio inserendo il file ajax con la variabile del modulo, quindi inserendo un parametro DIVERSO nello stesso file dicendo "passami il file che hai appena creato" (il percorso / nome è hardcoded nel file ajax).
Scott il

4
Ma invierà la richiesta 2 volte, non è corretto
Dharmendrasinh Chudasama

43

In realtà non hai affatto bisogno di ajax per questo. Se imposti "download.php" come href sul pulsante, oppure, se non è un link, usa:

window.location = 'download.php';

Il browser dovrebbe riconoscere il download binario e non caricare la pagina effettiva, ma solo fornire il file come download.


3
Il linguaggio di programmazione che stai utilizzando per modificare window.location è JavaScript.
mikemaccana

1
Hai ragione @mikemaccana, in realtà intendevo ajax :).
Jelle Kralt

2
Ho cercato una soluzione in alto e in basso e questo è così elegante e perfetto. Grazie mille.
Yangshun Tay

2
Ovviamente questa soluzione funzionerà solo se si tratta di un file statico già esistente.
krillgar

1
Se il server risponde con un errore, però, non ci sarà alcun modo per rimanere sulla tua pagina principale senza essere reindirizzato a una pagina di errore dal browser. Almeno questo è ciò che fa Chrome quando il risultato di window.location restituisce 404.
Ian

43

Per fare in modo che il browser scarichi un file è necessario effettuare la richiesta in questo modo:

 function downloadFile(urlToSend) {
     var req = new XMLHttpRequest();
     req.open("GET", urlToSend, true);
     req.responseType = "blob";
     req.onload = function (event) {
         var blob = req.response;
         var fileName = req.getResponseHeader("fileName") //if you have the fileName header available
         var link=document.createElement('a');
         link.href=window.URL.createObjectURL(blob);
         link.download=fileName;
         link.click();
     };

     req.send();
 }

7
Questo funziona per me, ma in Firefox, dovevo prima inserire un tag <a> nel DOM e fare riferimento ad esso come mio collegamento piuttosto che crearne uno al volo in modo che il file venga scaricato automaticamente.
Erik Donohoo

funziona ma cosa succede se il file viene creato durante l'esecuzione? non funziona come quando il file è già stato creato.
Diego

@ Taha L'ho provato su Edge e sembrava funzionare. Non so però di IE. Il mio cliente non si rivolge agli utenti di IE ;-) Sono fortunato? : D
Sнаđошƒаӽ

1
@ErikDonohoo Puoi creare il <a>tag con JS, anche "al volo", ma devi aggiungerlo a document.body. Sicuramente vuoi nasconderlo allo stesso tempo.
Márton Tamás

Grazie @ João, cercavo questa soluzione da molto tempo.
Abhishek Gharai

20

Soluzione cross browser, testata su Chrome, Firefox, Edge, IE11.

Nel DOM, aggiungi un tag di collegamento nascosto:

<a id="target" style="display: none"></a>

Poi:

var req = new XMLHttpRequest();
req.open("GET", downloadUrl, true);
req.responseType = "blob";
req.setRequestHeader('my-custom-header', 'custom-value'); // adding some headers (if needed)

req.onload = function (event) {
  var blob = req.response;
  var fileName = null;
  var contentType = req.getResponseHeader("content-type");

  // IE/EDGE seems not returning some response header
  if (req.getResponseHeader("content-disposition")) {
    var contentDisposition = req.getResponseHeader("content-disposition");
    fileName = contentDisposition.substring(contentDisposition.indexOf("=")+1);
  } else {
    fileName = "unnamed." + contentType.substring(contentType.indexOf("/")+1);
  }

  if (window.navigator.msSaveOrOpenBlob) {
    // Internet Explorer
    window.navigator.msSaveOrOpenBlob(new Blob([blob], {type: contentType}), fileName);
  } else {
    var el = document.getElementById("target");
    el.href = window.URL.createObjectURL(blob);
    el.download = fileName;
    el.click();
  }
};
req.send();

Buon codice generico. @leo puoi migliorare questo codice aggiungendo intestazioni personalizzate come Authorization?
vibs2006

1
Grazie @leo. È utile Inoltre, cosa suggerisci di aggiungere window.URL.revokeObjectURL(el.href);dopo el.click()?
vibs2006

Il nome del file sarà sbagliato se la disposizione del contenuto specifica un nome di file non UTF8.
Mathew Alden

14

È possibile. È possibile avviare il download dall'interno di una funzione ajax, ad esempio, subito dopo la creazione del file .csv.

Ho una funzione ajax che esporta un database di contatti in un file .csv e, subito dopo aver terminato, avvia automaticamente il download del file .csv. Quindi, dopo aver ricevuto responseText e tutto è a posto, reindirizzo il browser in questo modo:

window.location="download.php?filename=export.csv";

Il mio file download.php ha questo aspetto:

<?php

    $file = $_GET['filename'];

    header("Cache-Control: public");
    header("Content-Description: File Transfer");
    header("Content-Disposition: attachment; filename=".$file."");
    header("Content-Transfer-Encoding: binary");
    header("Content-Type: binary/octet-stream");
    readfile($file);

?>

Non c'è alcun aggiornamento della pagina e il download del file inizia automaticamente.

NOTA - Testato nei seguenti browser:

Chrome v37.0.2062.120 
Firefox v32.0.1
Opera v12.17
Internet Explorer v11

1
Non è pericoloso dal punto di vista della sicurezza?
Mickael Bergeron Néron

@ MickaelBergeronNéron Perché?
Pedro Sousa

5
Penso di sì perché chiunque può chiamare download.php? Filename = [qualcosa] e provare alcuni nomi di file e percorsi, specialmente quelli comuni, e questo potrebbe anche essere fatto all'interno di un ciclo all'interno di un programma o di uno script.
Mickael Bergeron Néron

.htaccess non lo eviterebbe?
Pedro Sousa

9
@PedroSousa .. no. htaccess controlla l'accesso alla struttura dei file tramite Apache. Poiché l'accesso ha raggiunto uno script PHP, htaccess interrompe ora il suo compito. Questo È MOLTO un buco di sicurezza perché in effetti, qualsiasi file che PHP (e l'utente con cui viene eseguito) può leggere, quindi può essere consegnato nel readfile ... Si dovrebbe sempre disinfettare il file richiesto da leggere
Prof83


0

Decodificare un nome di file dall'intestazione è un po 'più complesso ...

    var filename = "default.pdf";
    var disposition = req.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, '');
    }

3
Si prega di formattare l'intero blocco di codice e fornire alcune spiegazioni aggiuntive al processo per trarne vantaggio in futuro.
mickmackusa

0

Questa soluzione non è molto diversa da quelle sopra, ma per me funziona molto bene e penso sia pulita.

Suggerisco di codificare in base64 il lato file server (base64_encode (), se stai usando PHP) e di inviare i dati codificati in base64 al client

Sul client fai questo:

 let blob = this.dataURItoBlob(THE_MIME_TYPE + "," + response.file);
 let uri = URL.createObjectURL(blob);
 let link = document.createElement("a");
 link.download = THE_FILE_NAME,
 link.href = uri;
 document.body.appendChild(link);
 link.click();
 document.body.removeChild(link);

Questo codice inserisce i dati codificati in un collegamento e simula un clic sul collegamento, quindi lo rimuove.


Puoi semplicemente nascondere il tag a e popolare href dinamicamente. non è necessario aggiungere e rimuovere
BPeela

0

Le tue esigenze sono coperte da window.location('download.php');
Ma penso che devi passare il file da scaricare, non scaricare sempre lo stesso file, ed è per questo che stai usando una richiesta, un'opzione è creare un file php semplice come showfile.php e fare una richiesta come

var myfile = filetodownload.txt
var url = "shofile.php?file=" + myfile ;
ajaxRequest.open("GET", url, true);

showfile.php

<?php
$file = $_GET["file"] 
echo $file;

dove file è il nome del file passato tramite Get o Post nella richiesta e quindi cattura semplicemente la risposta in una funzione

if(ajaxRequest.readyState == 4){
                        var file = ajaxRequest.responseText;
                        window.location = 'downfile.php?file=' + file;  
                    }
                }

0

c'è un'altra soluzione per scaricare una pagina web in ajax. Ma mi riferisco a una pagina che deve essere prima elaborata e poi scaricata.

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

1) Nella chiamata ajax vengono effettuati solo i calcoli delle pagine.

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

       funzione (dati, stato) 
       {
            if (stato == "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: nel DownloadPage.php

    $ ID = $ _GET ["ID"];

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

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

    ...

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


0

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

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.

Ovviamente, consiglierei di controllare su quale browser stai sviluppando, poiché questo nuovo approccio non funzionerà su IE. È possibile trovare l'elenco completo della compatibilità del browser al seguente collegamento .

Importante : in questo esempio invio 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. Dato che sto inviando un JSON, devo aggiungere l' Content-Typeintestazione e impostarlo su application/json; charset=utf-8, in modo da far sapere al server il tipo di richiesta che riceverà.


0

La soluzione @Joao Marcos funziona per me ma ho dovuto modificare il codice per farlo funzionare su IE, di seguito se l'aspetto del codice

       downloadFile(url,filename) {
        var that = this;
        const extension =  url.split('/').pop().split('?')[0].split('.').pop();

        var req = new XMLHttpRequest();
        req.open("GET", url, true);
        req.responseType = "blob";
        req.onload = function (event) {
            const fileName = `${filename}.${extension}`;
            const blob = req.response;

            if (window.navigator.msSaveBlob) { // IE
                window.navigator.msSaveOrOpenBlob(blob, fileName);
            } 
            const link = document.createElement('a');
            link.href = window.URL.createObjectURL(blob);                
            link.download = fileName;
            link.click();
            URL.revokeObjectURL(link.href);

        };

        req.send();
    },
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.