404 votes

Télécharger un fichier par jQuery.Ajax

J'ai une action Struts2 du côté serveur pour le téléchargement de fichiers.

<action name="download" class="com.xxx.DownAction">
    <result name="success" type="stream">
        <param name="contentType">text/plain</param>
        <param name="inputName">imageStream</param>
        <param name="contentDisposition">attachment;filename={fileName}</param>
        <param name="bufferSize">1024</param>
    </result>
</action>

Cependant, lorsque j'appelle l'action à l'aide de la fonction jQuery :

$.post(
  "/download.action",{
    para1:value1,
    para2:value2
    ....
  },function(data){
      console.info(data);
   }
);

Dans Firebug, je vois que les données sont récupérées avec la balise Flux binaire . Je me demande comment ouvrir le fenêtre de téléchargement de fichiers avec lequel l'utilisateur peut sauvegarder le fichier localement ?

654voto

John Culviner Points 4878

Mise à jour des navigateurs modernes de 2019

C'est l'approche que je recommande maintenant, avec quelques réserves :

  • Un navigateur relativement moderne est nécessaire
  • Si le fichier est censé être très grande vous devriez probablement faire quelque chose de similaire à l'approche originale (iframe et cookie) car certaines des opérations ci-dessous pourraient probablement consommer de la mémoire système au moins aussi grande que le fichier téléchargé et/ou d'autres effets secondaires intéressants sur le CPU.

    fetch('https://jsonplaceholder.typicode.com/todos/1') .then(resp => resp.blob()) .then(blob => { const url = window.URL.createObjectURL(blob); const a = document.createElement('a'); a.style.display = 'none'; a.href = url; // the filename you want a.download = 'todo-1.json'; document.body.appendChild(a); a.click(); window.URL.revokeObjectURL(url); alert('your file has downloaded!'); // or you know, something with better UX... }) .catch(() => alert('oh no!'));

2012 Approche originale basée sur jQuery/iframe/Cookie

Bleuâtre a tout à fait raison à ce sujet, vous ne pouvez pas le faire via Ajax car JavaScript ne peut pas enregistrer de fichiers directement sur l'ordinateur d'un utilisateur (pour des raisons de sécurité). Malheureusement, le fait de pointer le de la fenêtre principale L'URL au niveau du téléchargement de votre fichier signifie que vous avez peu de contrôle sur l'expérience de l'utilisateur lorsqu'un téléchargement de fichier se produit.

J'ai créé Téléchargement du fichier jQuery qui permet une expérience de type "Ajax" pour les téléchargements de fichiers, avec des rappels OnSuccess et OnFailure pour une meilleure expérience utilisateur. Jetez un coup d'œil à mon article de blog sur le problème courant que le plugin résout et sur certaines façons de l'utiliser, ainsi qu'une Démonstration de jQuery File Download en action . Voici le source

Voici une démonstration d'un cas d'utilisation simple utilisant le plugin source avec des promesses. Le site Page de démonstration comprend également de nombreux autres exemples de "meilleure utilisation".

$.fileDownload('some/file.pdf')
    .done(function () { alert('File download a success!'); })
    .fail(function () { alert('File download failed!'); });

En fonction des navigateurs que vous devez prendre en charge, vous pourrez peut-être utiliser https://github.com/eligrey/FileSaver.js/ qui permet un contrôle plus explicite que la méthode IFRAME utilisée par jQuery File Download.

216voto

bluish Points 5503

_Personne n'a posté ceci La solution de @Pekka ... alors je vais le poster. Cela peut aider quelqu'un._

Il n'est pas nécessaire de le faire via Ajax. Utilisez simplement

window.location="download.action?para1=value1...."

27voto

Andrea Ligios Points 16653

1. Cadre agnostique : Servlet téléchargeant le fichier en pièce jointe

<!-- with JS -->
<a href="javascript:window.location='downloadServlet?param1=value1'">
    download
</a>

<!-- without JS -->
<a href="downloadServlet?param1=value1" >download</a>

2. Cadre Struts2 : Action de téléchargement du fichier en pièce jointe

<!-- with JS -->
<a href="javascript:window.location='downloadAction.action?param1=value1'">
    download
</a>

<!-- without JS -->
<a href="downloadAction.action?param1=value1" >download</a>

Il serait préférable d'utiliser <s:a> pointant avec OGNL à un URL créé avec <s:url> étiquette :

<!-- without JS, with Struts tags: THE RIGHT WAY -->    
<s:url action="downloadAction.action" var="url">
    <s:param name="param1">value1</s:param>
</s:ulr>
<s:a href="%{url}" >download</s:a>

Dans les cas ci-dessus, vous besoin de pour rédiger le Content-Disposition à l'en-tête réponse en précisant que le fichier doit être téléchargé ( attachment ) et non ouvert par le navigateur ( inline ). Vous besoin de pour spécifier le Type de contenu également, et vous voudrez peut-être ajouter le nom et la longueur du fichier (pour aider le navigateur à dessiner une barre de progression réaliste).

Par exemple, lors du téléchargement d'un ZIP :

response.setContentType("application/zip");
response.addHeader("Content-Disposition", 
                   "attachment; filename=\"name of my file.zip\"");
response.setHeader("Content-Length", myFile.length()); // or myByte[].length...

Avec Struts2 (sauf si vous utilisez l'action comme une servlet, un hack pour le streaming direct par exemple), il n'est pas nécessaire d'écrire directement quoi que ce soit dans la réponse ; il suffit d'utiliser la fonction Type de résultat du flux et le configurer dans struts.xml fonctionnera : EXEMPLE

<result name="success" type="stream">
   <param name="contentType">application/zip</param>
   <param name="contentDisposition">attachment;filename="${fileName}"</param>
   <param name="contentLength">${fileLength}</param>
</result>

3. Cadre agnostique (/ cadre Struts2) : Servlet(/Action) ouvrant le fichier à l'intérieur du navigateur

Si vous souhaitez ouvrir le fichier dans le navigateur, au lieu de le télécharger, le bouton Contenu-disposition doit être réglé sur en ligne mais la cible ne peut pas être l'emplacement de la fenêtre actuelle ; vous devez cibler une nouvelle fenêtre créée par javascript, une fenêtre de type <iframe> dans la page, ou une nouvelle fenêtre créée à la volée avec la mention "discuté" target="_blank" :

<!-- From a parent page into an IFrame without javascript -->   
<a href="downloadServlet?param1=value1" target="iFrameName">
    download
</a>

<!-- In a new window without javascript --> 
<a href="downloadServlet?param1=value1" target="_blank">
    download
</a>

<!-- In a new window with javascript -->    
<a href="javascript:window.open('downloadServlet?param1=value1');" >
    download
</a>

23voto

ndpu Points 8679

J'ai créé une petite fonction comme solution de contournement (inspirée par le plugin de @JohnCulviner) :

// creates iframe and form in it with hidden field,
// then submit form with provided data
// url - form url
// data - data to form field
// input_name - form hidden input name

function ajax_download(url, data, input_name) {
    var $iframe,
        iframe_doc,
        iframe_html;

    if (($iframe = $('#download_iframe')).length === 0) {
        $iframe = $("<iframe id='download_iframe'" +
                    " style='display: none' src='about:blank'></iframe>"
                   ).appendTo("body");
    }

    iframe_doc = $iframe[0].contentWindow || $iframe[0].contentDocument;
    if (iframe_doc.document) {
        iframe_doc = iframe_doc.document;
    }

    iframe_html = "<html><head></head><body><form method='POST' action='" +
                  url +"'>" +
                  "<input type=hidden name='" + input_name + "' value='" +
                  JSON.stringify(data) +"'/></form>" +
                  "</body></html>";

    iframe_doc.open();
    iframe_doc.write(iframe_html);
    $(iframe_doc).find('form').submit();
}

Démonstration avec événement de clic :

$('#someid').on('click', function() {
    ajax_download('/download.action', {'para1': 1, 'para2': 2}, 'dataname');
});

15voto

Shayne Points 494

Ok, basé sur le code de ndpu voici une version améliorée (je pense) de ajax_download;-

function ajax_download(url, data) {
    var $iframe,
        iframe_doc,
        iframe_html;

    if (($iframe = $('#download_iframe')).length === 0) {
        $iframe = $("<iframe id='download_iframe'" +
                    " style='display: none' src='about:blank'></iframe>"
                   ).appendTo("body");
    }

    iframe_doc = $iframe[0].contentWindow || $iframe[0].contentDocument;
    if (iframe_doc.document) {
        iframe_doc = iframe_doc.document;
    }

    iframe_html = "<html><head></head><body><form method='POST' action='" +
                  url +"'>" 

    Object.keys(data).forEach(function(key){
        iframe_html += "<input type='hidden' name='"+key+"' value='"+data[key]+"'>";

    });

        iframe_html +="</form></body></html>";

    iframe_doc.open();
    iframe_doc.write(iframe_html);
    $(iframe_doc).find('form').submit();
}

Utilisez ceci comme ceci;-

$('#someid').on('click', function() {
    ajax_download('/download.action', {'para1': 1, 'para2': 2});
});

Les paramètres sont envoyés sous forme de paramètres post appropriés, comme s'ils provenaient d'une entrée, plutôt que sous forme de chaîne encodée en json comme dans l'exemple précédent.

ATTENTION : Méfiez-vous du potentiel d'injection variable sur ces formulaires. Il existe peut-être un moyen plus sûr d'encoder ces variables. Vous pouvez aussi envisager de les échapper.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X