Scarica e apri il file PDF utilizzando Ajax


98

Ho una classe di azione che genera un PDF. Il contentTypeè impostato in modo appropriato.

public class MyAction extends ActionSupport 
{
   public String execute() {
    ...
    ...
    File report = signedPdfExporter.generateReport(xyzData, props);

    inputStream = new FileInputStream(report);
    contentDisposition = "attachment=\"" + report.getName() + "\"";
    contentType = "application/pdf";
    return SUCCESS;
   }
}

Lo chiamo action tramite una chiamata Ajax. Non conosco il modo per fornire questo flusso al browser. Ho provato alcune cose ma niente ha funzionato.

$.ajax({
    type: "POST",
    url: url,
    data: wireIdList,
    cache: false,
    success: function(response)
    {
        alert('got response');
        window.open(response);
    },
    error: function (XMLHttpRequest, textStatus, errorThrown) 
    {
        alert('Error occurred while opening fax template' 
              + getAjaxErrorString(textStatus, errorThrown));
    }
});

Quanto sopra dà l'errore:

Il tuo browser ha inviato una richiesta che questo server non poteva capire.

Risposte:


37

Non hai necessariamente bisogno di Ajax per questo. Basta un <a>collegamento se si imposta content-dispositiona attachmentnel codice lato server. In questo modo la pagina genitore rimarrà aperta, se questa fosse la tua principale preoccupazione (perché altrimenti avresti scelto Ajax inutilmente per questo?). Inoltre, non c'è modo di gestirlo in modo corretto e sincrono. PDF non sono dati di caratteri. Sono dati binari. Non puoi fare cose come $(element).load(). Si desidera utilizzare una richiesta completamente nuova per questo. Per questo <a href="pdfservlet/filename.pdf">pdf</a>è perfettamente adatto.

Per assisterti maggiormente con il codice lato server, dovrai dire di più sulla lingua utilizzata e pubblicare un estratto dei tentativi di codice.


7
Ancora una volta: non hai bisogno di Ajax per questo. Sta solo chiedendo guai. PDF sono dati binari, non dati di caratteri come HTML o JSON.
BalusC

3
var url = contextPath + "/xyz/blahBlah.action"; url + = url + "?" + params; prova {var child = window.open (url); child.focus (); } cattura (e) {}
Nayn

5
In alcuni browser il file window.open rimarrà aperto e vuoto, il che potrebbe essere fastidioso per gli utenti finali. Quindi, NON usare anche window.open per questo. Se content-dispositionè impostato su attachment, otterrai solo un Save asdialogo. La pagina principale rimarrà invariata. Solo <a href="pdfservlet/filename.pdf">pdf</a>o a <form action="pdfservlet/filename.pdf"><input type="submit"></form>è più che sufficiente.
BalusC

5
La lunghezza dell'URL è limitata. E l'autore sta chiedendo del POST.
Edward Olamisan

3
D'accordo con @EdwardOlamisan, questa non è una risposta corretta in quanto l'autore stava cercando di POSTdati.
adamj

123

Ecco come ho ottenuto questo funzionamento

$.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();
  }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

Risposta aggiornata utilizzando download.js

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


29
Funziona su Chrome? Riesco a vedere solo un pdf vuoto.
Tarun Gupta

1
Sì, funziona su tutti i browser moderni. Se vedi un pdf vuoto, prova a eseguire l'URL ajax in una nuova scheda. Se viene visualizzata anche una schermata vuota, potrebbe esserci un problema con il pdf stesso. Se vedi un file pdf lì e non nel file scaricato, fammelo sapere sulla mia e-mail. :)
Mayur Padshala

5
Questo (elemento di ancoraggio) in realtà non ha funzionato per me su IE 11, Edge e Firefox. cambiare il successo usando solo "window.open (URL.createObjectURL (blob))" ha funzionato.
JimiSweden

3
il file pdf viene scaricato ma nessun contenuto è disponibile. ho salvato byte [] sul lato server e il contenuto pdf è disponibile. suggerisci.
Awanish Kumar

4
il file pdf vuoto viene scaricato.
Farukh

31

Non credo proprio che nessuna delle risposte precedenti abbia individuato il problema del poster originale. Presumono tutti una richiesta GET mentre il poster stava tentando di inviare i dati e ottenere un download in risposta.

Durante la ricerca di una risposta migliore, abbiamo trovato questo plugin jQuery per la richiesta di download di file simili a Ajax .

Nel suo "cuore" crea un modulo HTML "temporaneo" contenente i dati forniti come campi di input. Questo modulo viene aggiunto al documento e inviato all'URL desiderato. Subito dopo il modulo viene nuovamente rimosso:

jQuery('<form action="'+ url +'" method="'+ (method||'post') +'">'+inputs+'</form>')
    .appendTo('body').submit().remove()

La risposta dell'aggiornamento di Mayur sembra piuttosto promettente e molto semplice rispetto al plug-in jQuery a cui ho fatto riferimento.


9

Ecco come risolvo questo problema.
La risposta di Jonathan Amend su questo post mi ha aiutato molto.
L'esempio seguente è semplificato.

Per maggiori dettagli, il codice sorgente sopra è in grado di scaricare un file utilizzando una richiesta JQuery Ajax (GET, POST, PUT ecc . ) . Inoltre, aiuta a caricare i parametri come JSON e a cambiare il tipo di contenuto in application / json (il mio predefinito) .

La fonte html :

<form method="POST">
    <input type="text" name="startDate"/>
    <input type="text" name="endDate"/>
    <input type="text" name="startDate"/>
    <select name="reportTimeDetail">
        <option value="1">1</option>
    </select>
    <button type="submit"> Submit</button>
</form>  

Un semplice modulo con due testo di input, un elemento di selezione e un elemento pulsante.

La sorgente della pagina javascript :

<script type="text/javascript" src="JQuery 1.11.0 link"></script>
<script type="text/javascript">
    // File Download on form submition.
    $(document).on("ready", function(){
        $("form button").on("click", function (event) {
            event.stopPropagation(); // Do not propagate the event.

            // Create an object that will manage to download the file.
            new AjaxDownloadFile({
                url: "url that returns a file",
                data: JSON.stringify($("form").serializeObject())
            });

            return false; // Do not submit the form.
        });
    });
</script>  

Un semplice evento al clic del pulsante. Crea un oggetto AjaxDownloadFile. L'origine della classe AjaxDownloadFile è di seguito.

L' origine della classe AjaxDownloadFile :

var AjaxDownloadFile = function (configurationSettings) {
    // Standard settings.
    this.settings = {
        // JQuery AJAX default attributes.
        url: "",
        type: "POST",
        headers: {
            "Content-Type": "application/json; charset=UTF-8"
        },
        data: {},
        // Custom events.
        onSuccessStart: function (response, status, xhr, self) {
        },
        onSuccessFinish: function (response, status, xhr, self, filename) {
        },
        onErrorOccured: function (response, status, xhr, self) {
        }
    };
    this.download = function () {
        var self = this;
        $.ajax({
            type: this.settings.type,
            url: this.settings.url,
            headers: this.settings.headers,
            data: this.settings.data,
            success: function (response, status, xhr) {
                // Start custom event.
                self.settings.onSuccessStart(response, status, xhr, self);

                // Check if a filename is existing on the response headers.
                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
                }

                // Final custom event.
                self.settings.onSuccessFinish(response, status, xhr, self, filename);
            },
            error: function (response, status, xhr) {
                // Custom event to handle the error.
                self.settings.onErrorOccured(response, status, xhr, self);
            }
        });
    };
    // Constructor.
    {
        // Merge settings.
        $.extend(this.settings, configurationSettings);
        // Make the request.
        this.download();
    }
};

Ho creato questa classe da aggiungere alla mia libreria JS. È riutilizzabile. Spero che aiuti.


2
Bloboggetto è supportato in IE10 +.
schiacciare l'

Ho dovuto impostare responseTypexhr su arraybuffero blobperché funzionasse. (Altrimenti, funziona benissimo.)
tjklemz

Ho avuto la stessa identica domanda. Tutte le persone che rispondono "crea solo un collegamento" non aiutano l'OP. Se il tuo contenuto è dinamico e il link a cui stai andando è dinamico, devi jquery tutto ... la risposta per me è stata mettere un modulo sulla pagina con tutti gli input nascosti (non viene mostrato all'utente) e quindi compilarlo e inviarlo con jquery. Funziona alla grande.
Scott

Questa è un'ottima risposta, ma per qualche motivo continuo a ricevere PDF vuoti. Non riesco a capirlo. Quando restituisco lo stesso set di byte tramite l'API, va bene, quindi ha a che fare con la risposta MVC. Uso il tipo di risposta FileResult: File (bytes, System.Net.Mime.MediaTypeNames.Application.Octet, fileName);
Jurijs Kastanovs

Chiarimento: se si apre l'URL tramite la barra degli indirizzi, il file è aperto correttamente. Se utilizzo AJAX + blob per ottenere il file, il file non è valido.
Jurijs Kastanovs

7

Quello che ha funzionato per me è il codice seguente, poiché la funzione server sta recuperando File(memoryStream.GetBuffer(), "application/pdf", "fileName.pdf");:

$http.get( fullUrl, { responseType: 'arraybuffer' })
            .success(function (response) {
                var blob = new Blob([response], { type: 'application/pdf' });

                if (window.navigator && window.navigator.msSaveOrOpenBlob) {
                    window.navigator.msSaveOrOpenBlob(blob); // for IE
                }
                else {
                    var fileURL = URL.createObjectURL(blob);
                    var newWin = window.open(fileURL);
                    newWin.focus();
                    newWin.reload();
                }
});

Questo funziona perfettamente per me al momento di questo commento e del nuovissimo Chrome
Loredra L

6

Potresti usare questo plugin che crea un modulo e lo invia, quindi lo rimuove dalla pagina.

jQuery.download = function(url, data, method) {
    //url and data options required
    if (url && data) {
        //data can be string of parameters or array/object
        data = typeof data == 'string' ? data : jQuery.param(data);
        //split params into form inputs
        var inputs = '';
        jQuery.each(data.split('&'), function() {
            var pair = this.split('=');
            inputs += '<input type="hidden" name="' + pair[0] +
                '" value="' + pair[1] + '" />';
        });
        //send request
        jQuery('<form action="' + url +
                '" method="' + (method || 'post') + '">' + inputs + '</form>')
            .appendTo('body').submit().remove();
    };
};


$.download(
    '/export.php',
    'filename=mySpreadsheet&format=xls&content=' + spreadsheetData
);

Questo ha funzionato per me. Ho trovato questo plugin qui


Questo plugin crea semplicemente un modulo e lo invia, quindi lo rimuove dalla pagina. (se qualcuno se lo stesse chiedendo)
schiaccia il

4

Il codice seguente ha funzionato per me

//Parameter to be passed
var data = 'reportid=R3823&isSQL=1&filter=[]';
var xhr = new XMLHttpRequest();
xhr.open("POST", "Reporting.jsp"); //url.It can pdf file path
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.responseType = "blob";
xhr.onload = function () {
    if (this.status === 200) {
        var blob = new Blob([xhr.response]);
        const url = window.URL.createObjectURL(blob);
        var a = document.createElement('a');
        a.href = url;
        a.download = 'myFile.pdf';
        a.click();
        setTimeout(function () {
            // For Firefox it is necessary to delay revoking the ObjectURL
            window.URL.revokeObjectURL(data)
                , 100
        })
    }
};
xhr.send(data);

4

Per risolvere il problema del PDF vuoto nella richiesta post per ottenere dati di flusso come PDF, è necessario aggiungere il tipo di risposta come "arraybuffer" o "blob" nella richiesta

$.ajax({
  url: '<URL>',
  type: "POST",
  dataType: 'arraybuffer',
  success: function(data) {
    let blob = new Blob([data], {type: 'arraybuffer'});
    let link = document.createElement('a');
    let objectURL = window.URL.createObjectURL(blob);
    link.href = objectURL;
    link.target = '_self';
    link.download = "fileName.pdf";
    (document.body || document.documentElement).appendChild(link);
    link.click();
    setTimeout(()=>{
        window.URL.revokeObjectURL(objectURL);
        link.remove();
    }, 100);
  }
});

3

Per quanto riguarda la risposta data da Mayur Padshala questa è la logica corretta per scaricare un file pdf tramite ajax ma come altri riportano nei commenti questa soluzione è in effetti scarica un pdf vuoto.

La ragione di ciò è spiegata nella risposta accettata a questa domanda : jQuery ha alcuni problemi nel caricare i dati binari utilizzando le richieste AJAX, poiché non implementa ancora alcune funzionalità di HTML5 XHR v2, vedere questa richiesta di miglioramento e questa discussione .

Quindi l'utilizzo HTMLHTTPRequestdel codice dovrebbe essere simile a questo:

var req = new XMLHttpRequest();
req.open("POST", "URL", true);
req.responseType = "blob";
req.onload = function (event) {
    var blob = req.response;
    var link=document.createElement('a');
    link.href=window.URL.createObjectURL(blob);
    link.download="name_for_the_file_to_save_with_extention";
    link.click();
}

2

crea un iframe nascosto, quindi nel tuo codice ajax sopra:

URL: document.getElementById('myiframeid').src = your_server_side_url ,

e rimuovere il file window.open(response);


Questa soluzione ha funzionato a meraviglia. Sto chiamando uno script lato server che effettua una chiamata curl a un servizio che recupera il file tramite curl. Funziona benissimo in quanto posso rilasciare una gif in caricamento e disabilitare il collegamento della richiesta.
pasta d'uovo l'

1
Questa soluzione funziona per le richieste GET non per le richieste POST come nel post originale.
chiccodoro

2

Questo frammento è per gli utenti js angolari che dovranno affrontare lo stesso problema, si noti che il file di risposta viene scaricato utilizzando un evento di clic programmato. In questo caso, le intestazioni sono state inviate dal server contenente il nome del file e il contenuto / tipo.

$http({
    method: 'POST', 
    url: 'DownloadAttachment_URL',
    data: { 'fileRef': 'filename.pdf' }, //I'm sending filename as a param
    headers: { 'Authorization': $localStorage.jwt === undefined ? jwt : $localStorage.jwt },
    responseType: 'arraybuffer',
}).success(function (data, status, headers, config) {
    headers = headers();
    var filename = headers['x-filename'];
    var contentType = headers['content-type'];
    var linkElement = document.createElement('a');
    try {
        var blob = new Blob([data], { type: contentType });
        var url = window.URL.createObjectURL(blob);

        linkElement.setAttribute('href', url);
        linkElement.setAttribute("download", filename);

        var clickEvent = new MouseEvent("click", {
            "view": window,
            "bubbles": true,
            "cancelable": false
        });
        linkElement.dispatchEvent(clickEvent);
    } catch (ex) {
        console.log(ex);
    }
}).error(function (data, status, headers, config) {
}).finally(function () {

});

Per favore scrivi qualche spiegazione per la tua risposta.
Gufran Hasan

1

Devi farlo con l'Ajax? Non potrebbe essere possibile caricarlo in un iframe?


1
Sto verificando se è possibile farlo con Ajax. Se è tecnicamente impossibile o un approccio inferiore, passerei ad altri approcci.
Nayn

1

Spero che questo ti farà risparmiare qualche ora e ti eviterà il mal di testa. Mi ci è voluto un po 'per capirlo, ma la normale richiesta $ .ajax () ha rovinato il mio file PDF, mentre la richiesta tramite la barra degli indirizzi ha funzionato perfettamente. La soluzione era questa:

Includi download.js: http://danml.com/download.html

Quindi usa XMLHttpRequest invece della richiesta $ .ajax ().

    var ajax = new XMLHttpRequest(); 

    ajax.open("GET", '/Admin/GetPdf' + id, true); 
    ajax.onreadystatechange = function(data) { 
        if (this.readyState == 4)
        {
            if (this.status == 200)
            {
                download(this.response, "report.pdf", "application/pdf");

            }
            else if (this.responseText != "")
            {
                alert(this.responseText);
            }
        }
        else if (this.readyState == 2)
        {
            if (this.status == 200)
            {
                this.responseType = "blob";
            }
            else
            {
                this.responseType = "text";
            }
        }
    };

    ajax.send(null);

0

var xhr;
var beforeSend = function(){
    $('#pleasewaitDL').modal('show');
}
$(function () {
    $('#print_brochure_link').click(function(){
        beforeSend();
        xhr = new XMLHttpRequest();
        xhr.open("GET",$('#preparedPrintModalForm').attr('action'), true); 
        xhr.responseType = "blob";
        xhr.onload = function (e) {
            if (this.status === 200) {
                var file = window.URL.createObjectURL(this.response);
                var a = document.createElement("a");
                a.href = file;
                a.download = this.response.name || "Property Brochure";
                console.log(file);
                document.body.appendChild(a);
                a.click();
                
                window.onfocus = function () {                     
                  document.body.removeChild(a)
                }
                $('#pleasewaitDL').modal('hide');
            };
        };
        xhr.send($('#preparedPrintModalForm').serialize());
    });
    $('#pleasewaitDLCancel').click(function() {
        xhr.abort();
    });
});


0

Se devi lavorare con il flusso di file (quindi nessun PDF salvato fisicamente) come facciamo noi e vuoi scaricare il PDF senza ricaricare la pagina, la seguente funzione funziona per noi:

HTML

<div id="download-helper-hidden-container" style="display:none">
     <form id="download-helper-form" target="pdf-download-output" method="post">
            <input type="hidden" name="downloadHelperTransferData" id="downloadHelperTransferData" />
     </form>
     <iframe id="pdf-helper-output" name="pdf-download-output"></iframe>
</div>

Javascript

var form = document.getElementById('download-helper-form');
$("#downloadHelperTransferData").val(transferData);
form.action = "ServerSideFunctionWhichWritesPdfBytesToResponse";
form.submit();

A causa di target = "pdf-download-output" , la risposta viene scritta nell'iframe e quindi non viene eseguito alcun ricaricamento della pagina, ma il pdf-response-stream viene emesso nel browser come download.


scusa, ma come si ottiene il valore transferData?
Kate
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.