web-dev-qa-db-fra.com

Téléchargez et ouvrez PDF fichier utilisant Ajax

J'ai une classe d'action qui génère un PDF. La contentType est définie correctement.

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

J'appelle cette action via un appel Ajax. Je ne connais pas le moyen de transmettre ce flux au navigateur. J'ai essayé quelques choses mais rien n'a fonctionné.

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

Ce qui précède donne l'erreur:

Votre navigateur a envoyé une demande que ce serveur n'a pas pu comprendre.

79
Nayn

Vous n'avez pas nécessairement besoin d'Ajax pour cela. Un simple lien <a> suffit si vous définissez content-disposition sur attachment dans le code côté serveur. De cette façon, la page parent restera simplement ouverte, si telle était votre préoccupation principale (pourquoi auriez-vous inutilement choisi Ajax pour cela sinon?). En outre, il n’existe aucun moyen de gérer cela de manière parfaitement synchronisée. PDF n'est pas une donnée de caractère. Ce sont des données binaires. Vous ne pouvez pas faire des choses comme $(element).load(). Vous voulez utiliser complètement nouveau request pour cela. Pour cela, <a href="pdfservlet/filename.pdf">pdf</a> convient parfaitement.

Pour vous aider davantage avec le code côté serveur, vous devez en dire plus sur la langue utilisée et publier un extrait des tentatives de code.

33
BalusC

Voici comment j'ai obtenu ce travail

$.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>

Réponse mise à jour avec download.js

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

110
Mayur Padshala

Je ne pense pas vraiment que les réponses précédentes aient identifié le problème de l'affiche originale. Ils supposent tous une demande GET pendant que l'affiche tentait POST - de fournir des données et d'obtenir un téléchargement en réponse.

En cherchant une meilleure réponse, nous avons trouvé ceci Plugin jQuery pour demander des téléchargements de fichiers de type Ajax .

Dans son "coeur", il crée un formulaire HTML "temporaire" contenant les données fournies en tant que champs de saisie. Ce formulaire est ajouté au document et envoyé à l'URL souhaitée. Juste après que le formulaire est à nouveau supprimé:

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

Mise à jour La réponse de Mayur semble prometteuse et très simple en comparaison du plug-in jQuery auquel j'ai fait référence.

30
chiccodoro

Voici comment je résous ce problème.
La réponse de Jonathan Amend sur ce post m'a beaucoup aidé.
L'exemple ci-dessous est simplifié. 

Pour plus de détails, le code source ci-dessus est capable de télécharger un fichier à l’aide d’une requête JQuery Ajax (GET, POST, PUT, etc.). Cela permet également de télécharger des paramètres sous la forme JSON et de changer le type de contenu en application/json (ma valeur par défaut).

La source 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 formulaire simple avec deux textes d'entrée, un élément select et un élément de bouton.

La source page 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 simple événement sur le clic du bouton. Il crée un objet AjaxDownloadFile. La source de la classe AjaxDownloadFile est ci-dessous. 

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

J'ai créé cette classe pour l'ajouter à ma bibliothèque JS. C'est réutilisable. J'espère que cela pourra aider.

9
George Siggouroglou

Ce qui a fonctionné pour moi est le code suivant, car la fonction serveur récupère 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();
                }
});
5
ParPar

Vous pouvez utiliser ce plugin qui crée un formulaire, le soumet puis le supprime de la page.

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

Cela a fonctionné pour moi. Trouvé ce plugin ici

5
Ijas Ameenudeen

créez une iframe cachée, puis dans votre code ajax ci-dessus:

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

et retirez la window.open(response);

2
qalhat

Devez-vous le faire avec Ajax? Ne pourrait-il pas être possible de le charger dans un iframe?

1
Emil Vikström

Pour résoudre le problème vierge PDF dans une demande ultérieure permettant d'obtenir des données de flux telles que PDF, nous devons ajouter un type de réponse en tant que "arraybuffer" ou "blob" dans la demande.

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

Ce fragment de code est destiné aux utilisateurs de js angulaires qui rencontreront le même problème, mais notez que le fichier de réponses est téléchargé à l'aide d'un événement de clic programmé .

$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 () {

});
1
Gihan Sandaru

Concernant la réponse donnée par Mayur Padshala , c’est la bonne logique pour télécharger un fichier pdf via ajax, mais comme d’autres le rapportent dans les commentaires, cette solution consiste en fait à télécharger un fichier pdf vierge.

La raison en est expliquée dans la réponse acceptée de cette question question : jQuery a quelques problèmes lors du chargement de données binaires à l’aide de requêtes AJAX, car il n’implémente pas encore certaines fonctionnalités HTML5 XHR v2. Voir cette amélioration demande et cette discussion .

Donc, en utilisant HTMLHTTPRequest, le code devrait ressembler à ceci:

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

Le code suivant a fonctionné pour moi

//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);
1
MemZ

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
POGSNET

Si vous devez travailler avec le flux de fichiers (donc aucun PDF enregistré physiquement) comme nous le faisons et que vous souhaitez télécharger le fichier PDF sans rechargement de page, la fonction suivante fonctionne pour nous:

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

En raison de target = "pdf-download-output", la réponse est écrite dans l'iframe et, par conséquent, aucun rechargement de page n'est exécuté, mais le flux de réponses pdf est généré dans le navigateur sous forme de téléchargement.

0
George Maharis

J'espère que cela vous fera gagner quelques heures et vous évitera un mal de tête ..__ Il m'a fallu un certain temps pour le comprendre, mais le fait de demander régulièrement $ .ajax () a ruiné mon fichier PDF, tout en le demandant par le biais de La barre d'adresse fonctionnait parfaitement ... La solution était la suivante:

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

Ensuite, utilisez XMLHttpRequest au lieu de la requête $ .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
Jurijs Kastanovs