web-dev-qa-db-fra.com

démarrage du téléchargement de fichiers avec JavaScript

Disons que j'ai des liens de téléchargement pour les fichiers sur mon site.

Lorsque vous cliquez sur ces liens, envoyez une demande AJAX au serveur qui renvoie l'URL avec l'emplacement du fichier.

Ce que je veux faire, c'est demander au navigateur de télécharger le fichier lorsque la réponse sera de retour. Existe-t-il un moyen portable de le faire?

54
Vasil

Essayez cette lib https://github.com/PixelsCommander/Download-File-JS c'est plus moderne que toutes les solutions décrites précédemment car utilise l'attribut "download" et une combinaison de méthodes pour apporter la meilleure expérience possible .

Expliqué ici - http://pixelscommander.com/en/javascript/javascript-file-downliading-ignore-content-type/

Semble être un morceau de code idéal pour commencer le téléchargement en JavaScript.

4
PixelCommander

Nous procédons de cette façon: ajoutez d'abord ce script.

<script type="text/javascript">
function populateIframe(id,path) 
{
    var ifrm = document.getElementById(id);
    ifrm.src = "download.php?path="+path;
}
</script>

Placez-le où vous voulez le bouton de téléchargement (ici, nous utilisons juste un lien):

<iframe id="frame1" style="display:none"></iframe>
<a href="javascript:populateIframe('frame1','<?php echo $path; ?>')">download</a>

Le fichier 'download.php' (qui doit être placé sur votre serveur) contient simplement:

<?php 
   header("Content-Type: application/octet-stream");
   header("Content-Disposition: attachment; filename=".$_GET['path']);
   readfile($_GET['path']);
?>

Ainsi, lorsque vous cliquez sur le lien, l'iframe caché obtient/ouvre le fichier source 'download.php'. Avec le chemin d'accès comme paramètre get. Nous pensons que c'est la meilleure solution!

Il convient de noter que la partie PHP de cette solution est une démonstration simple et potentiellement très, très peu sûre. Elle permet à l'utilisateur de télécharger n'importe quel fichier, pas seulement un ensemble prédéfini. Cela signifie ils pourraient télécharger des parties du code source du site lui-même, contenant éventuellement des informations d'identification API, etc.

24
roulio

J'ai créé un open source plugin jQuery File Download ( Démo avec des exemples ) ( GitHub ) qui pourrait également vous aider dans votre situation. Il fonctionne de manière assez similaire avec un iframe mais a quelques fonctionnalités intéressantes que j'ai trouvées assez pratiques:

  • L'utilisateur ne quitte jamais la même page à partir de laquelle il a lancé le téléchargement d'un fichier. Cette fonctionnalité devient cruciale pour les applications Web modernes
  • Prise en charge multi-navigateurs testée (y compris mobile!)
  • Il prend en charge POST et GET les demandes d'une manière similaire à jQuery AJAX API
  • les fonctions successCallback et failCallback vous permettent d'être explicite sur ce que l'utilisateur voit dans l'une ou l'autre situation
  • En conjonction avec jQuery UI, un développeur peut facilement afficher un modal indiquant à l'utilisateur qu'un téléchargement de fichier est en cours, dissoudre le modal après le début du téléchargement ou même informer l'utilisateur de manière conviviale qu'une erreur s'est produite. Voir la démo pour un exemple de cela.
18
John Culviner

En lisant les réponses - y compris celle acceptée, je voudrais souligner les implications en matière de sécurité de la transmission directe d'un chemin au fichier de lecture via GET.

Cela peut sembler évident pour certains, mais certains peuvent simplement copier/coller ce code:

<?php 
   header("Content-Type: application/octet-stream");
   header("Content-Disposition: attachment; filename=".$_GET['path']);
   readfile($_GET['path']);
?>

Que se passe-t-il si je passe quelque chose comme '/ path/to/fileWithSecrets' à ce script? Le script donné enverra avec plaisir tout fichier auquel l'utilisateur du serveur Web a accès.

Veuillez vous référer à cette discussion pour savoir comment éviter cela: Comment puis-je m'assurer qu'un chemin de fichier se trouve dans un sous-répertoire donné?

14
DanielKhan

Si c'est votre propre application serveur, je suggère d'utiliser l'en-tête suivant

Content-disposition: attachment; filename=fname.ext

Cela forcera tout navigateur à télécharger le fichier et non à le rendre dans la fenêtre du navigateur.

10
fasih.rana

Il suffit d'appeler window.location.href = new_url à partir de votre javascript et il redirigera le navigateur vers cette URL telle que l'utilisateur l'a tapée dans la barre d'adresse

7
Gareth

Acceptez les méthodes mentionnées par maxnk, mais vous voudrez peut-être reconsidérer la tentative de forcer automatiquement le navigateur à télécharger l'URL. Cela peut fonctionner correctement pour les fichiers binaires mais pour d'autres types de fichiers (texte, PDF, images, vidéo), le navigateur peut vouloir le rendre dans la fenêtre (ou IFRAME) plutôt que de l'enregistrer sur le disque.

Si vous avez vraiment besoin d'appeler Ajax pour obtenir les liens de téléchargement finaux, qu'en est-il de l'utilisation de DHTML pour écrire dynamiquement le lien de téléchargement (à partir de la réponse ajax) dans la page? De cette façon, l'utilisateur pourrait soit cliquer dessus pour télécharger (si binaire) ou afficher dans son navigateur - ou sélectionner "Enregistrer sous" sur le lien pour enregistrer sur le disque. C'est un clic supplémentaire, mais l'utilisateur a plus de contrôle.

3
Marc Novakowski

Pour contourner la faille de sécurité dans la réponse la plus votée, vous pouvez définir le iframe src directement sur le fichier que vous souhaitez (au lieu d'un fichier php intermédiaire) et définir les informations d'en-tête dans un fichier .htaccess:

<Files *.apk>
     ForceType application/force-download
     Header set Content-Disposition attachment
     Header set Content-Type application/vnd.Android.package-archive
     Header set Content-Transfer-Encoding binary
</Files>
3
Matt K

En ce qui concerne la première réponse, j'ai une solution possible au risque de sécurité.

<?php
     if(isset($_GET['path'])){
         if(in_array($_GET['path'], glob("*/*.*"))){
             header("Content-Type: application/octet-stream");
             header("Content-Disposition: attachment; filename=".$_GET['path']);
             readfile($_GET['path']);
         }
     }
?>

En utilisant la fonction glob () (j'ai testé le fichier de téléchargement dans un chemin d'accès d'un dossier à partir du fichier à télécharger), j'ai pu créer un tableau rapide de fichiers qui sont "autorisés" à être téléchargés et j'ai vérifié le chemin d'accès par rapport à celui-ci . Cela garantit non seulement que le fichier saisi n'est pas quelque chose de sensible, mais vérifie également l'existence des fichiers en même temps.

~ Remarque: Javascript/HTML ~

HTML:

<iframe id="download" style="display:none"></iframe>

et

<input type="submit" value="Download" onclick="ChangeSource('document_path');return false;">

JavaScript:

<script type="text/javascript">
    <!--
        function ChangeSource(path){
            document.getElementByID('download').src = 'path_to_php?path=' + document_path;
        }
    -->
</script>
2
James Bordine II

Je suggère de créer un iframe invisible sur la page et de définir son src sur l'URL que vous avez reçue du serveur - le téléchargement commencera sans rechargement de page.

Ou vous pouvez simplement définir le document.location.href actuel sur l'adresse URL reçue. Mais cela peut amener l'utilisateur à voir une erreur si le document demandé n'existe pas.

2
maxnk

Pourquoi faites-vous des choses côté serveur alors que tout ce dont vous avez besoin est de rediriger le navigateur vers différents window.location.href?

Voici le code qui analyse? File = QueryString ( tiré de cette question ) et redirige l'utilisateur vers cette adresse en 1 seconde (fonctionne pour moi même sur Android):

<script type="text/javascript">
    var urlParams;
    (window.onpopstate = function () {
        var match,
        pl = /\+/g,  // Regex for replacing addition symbol with a space
        search = /([^&=]+)=?([^&]*)/g,
        decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
        query = window.location.search.substring(1);

        urlParams = {};
        while (match = search.exec(query))
            urlParams[decode(match[1])] = decode(match[2]);
    })();

    (window.onload = function() {
        var path = urlParams["file"];
        setTimeout(function() { document.location.href = path; }, 1000);
    });
</script>

Si vous avez jQuery dans votre projet, supprimez définitivement ces gestionnaires window.onpopstate & window.onload et faites tout dans $ (document) .ready (function () {});

1
nikib3ro

Je suggère window.open() pour ouvrir une fenêtre contextuelle. S'il s'agit d'un téléchargement, il n'y aura pas de fenêtre et vous obtiendrez votre fichier. S'il y a un 404 ou quelque chose, l'utilisateur le verra dans une nouvelle fenêtre (par conséquent, son travail ne sera pas dérangé, mais il recevra toujours un message d'erreur).

1
Vilx-