web-dev-qa-db-fra.com

Comment servir un fichier au téléchargement avec AngularJS ou Javascript?

J'ai du texte dans une textarea cachée. Quand un bouton est cliqué, j'aimerais que le texte proposé au téléchargement soit un fichier .txt. Est-ce possible d'utiliser AngularJS ou Javascript?

93
nickponline

Vous pouvez faire quelque chose comme ceci en utilisant Blob.

<a download="content.txt" ng-href="{{ url }}">download</a>

dans votre contrôleur:

var content = 'file content for example';
var blob = new Blob([ content ], { type : 'text/plain' });
$scope.url = (window.URL || window.webkitURL).createObjectURL( blob );

afin d'activer l'URL:

app = angular.module(...);
app.config(['$compileProvider',
    function ($compileProvider) {
        $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|tel|file|blob):/);
}]);

S'il vous plaît noter que 

Chaque fois que vous appelez createObjectURL (), une nouvelle URL d'objet est créée, même si vous en avez déjà créée une pour le même objet. Chacun de ceux-ci doit être libéré en appelant URL.revokeObjectURL () lorsque vous n'en avez plus besoin. Les navigateurs les publient automatiquement lorsque le document est déchargé. Toutefois, pour optimiser les performances et l'utilisation de la mémoire, vous devez le faire s'il existe des moments sûrs où vous pouvez les décharger explicitement.

Source: MDN

106
Tosh

Essaye ça 

<a target="_self" href="mysite.com/uploads/ahlem.pdf" download="foo.pdf">

et visitez ce site cela pourrait vous être utile :) 

http://docs.angularjs.org/guide/

26
AhlemMustapha

Cela peut être fait en javascript sans avoir à ouvrir une autre fenêtre de navigateur.

window.location.assign('url');

Remplacez "URL" par le lien vers votre fichier. Vous pouvez mettre ceci dans une fonction et l'appeler avec ng-click si vous devez déclencher le téléchargement depuis un bouton.

21
mm8154

Dans notre projet actuel au travail, nous avions un iFrame invisible et je devais fournir l'URL du fichier à l'iFrame pour obtenir une boîte de dialogue de téléchargement. Lorsque vous cliquez sur le bouton, le contrôleur génère l’URL dynamique et déclenche un événement $ scope dans lequel figure une liste directive personnalisée que j’ai écrite. La directive ajoutera un iFrame au corps s'il n'existe pas déjà et définit l'attribut url dessus.

EDIT: Ajouter une directive

appModule.directive('fileDownload', function ($compile) {
    var fd = {
        restrict: 'A',
        link: function (scope, iElement, iAttrs) {

            scope.$on("downloadFile", function (e, url) {
                var iFrame = iElement.find("iframe");
                if (!(iFrame && iFrame.length > 0)) {
                    iFrame = $("<iframe style='position:fixed;display:none;top:-1px;left:-1px;'/>");
                    iElement.append(iFrame);
                }

                iFrame.attr("src", url);


            });
        }
    };

    return fd;
});

Cette directive répond à un événement du contrôleur appelé downloadFile

donc dans votre contrôleur vous faites

$scope.$broadcast("downloadFile", url);
14
Ketan

Vous pouvez définir location.href sur un data URI contenant les données que vous souhaitez laisser l'utilisateur télécharger. En plus de cela, je ne pense pas qu'il soit possible de le faire avec du JavaScript uniquement.

12
Jani Hartikainen

Voudrais juste ajouter que s'il ne télécharge pas le fichier pour des raisons dangereuses: blob: null ... lorsque vous passez le curseur de la souris sur le bouton de téléchargement, vous devez le nettoyer. Par exemple,

var app = angular.module ('app', []);

app.config (function ($ compileProvider) {

$compileProvider.aHrefSanitizationWhitelist(/^\s*(|blob|):/);
6
Samir Alajmovic

J'avais le même problème et passais de nombreuses heures à trouver différentes solutions, et maintenant, je rejoins tous les commentaires de ce post.

HTML:

<a href="#" class="btn btn-default" file-name="'fileName.extension'"  ng-click="getFile()" file-download="myBlobObject"><i class="fa fa-file-Excel-o"></i></a>

DIRECTIVE:

directive('fileDownload',function(){
    return{
        restrict:'A',
        scope:{
            fileDownload:'=',
            fileName:'=',
        },

        link:function(scope,elem,atrs){


            scope.$watch('fileDownload',function(newValue, oldValue){

                if(newValue!=undefined && newValue!=null){
                    console.debug('Downloading a new file'); 
                    var isFirefox = typeof InstallTrigger !== 'undefined';
                    var isSafari = Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0;
                    var isIE = /*@cc_on!@*/false || !!document.documentMode;
                    var isEdge = !isIE && !!window.StyleMedia;
                    var isChrome = !!window.chrome && !!window.chrome.webstore;
                    var isOpera = (!!window.opr && !!opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0;
                    var isBlink = (isChrome || isOpera) && !!window.CSS;

                    if(isFirefox || isIE || isChrome){
                        if(isChrome){
                            console.log('Manage Google Chrome download');
                            var url = window.URL || window.webkitURL;
                            var fileURL = url.createObjectURL(scope.fileDownload);
                            var downloadLink = angular.element('<a></a>');//create a new  <a> tag element
                            downloadLink.attr('href',fileURL);
                            downloadLink.attr('download',scope.fileName);
                            downloadLink.attr('target','_self');
                            downloadLink[0].click();//call click function
                            url.revokeObjectURL(fileURL);//revoke the object from URL
                        }
                        if(isIE){
                            console.log('Manage IE download>10');
                            window.navigator.msSaveOrOpenBlob(scope.fileDownload,scope.fileName); 
                        }
                        if(isFirefox){
                            console.log('Manage Mozilla Firefox download');
                            var url = window.URL || window.webkitURL;
                            var fileURL = url.createObjectURL(scope.fileDownload);
                            var a=elem[0];//recover the <a> tag from directive
                            a.href=fileURL;
                            a.download=scope.fileName;
                            a.target='_self';
                            a.click();//we call click function
                        }


                    }else{
                        alert('SORRY YOUR BROWSER IS NOT COMPATIBLE');
                    }
                }
            });

        }
    }
})

DANS LE CONTROLEUR:

$scope.myBlobObject=undefined;
$scope.getFile=function(){
        console.log('download started, you can show a wating animation');
        serviceAsPromise.getStream({param1:'data1',param1:'data2', ...})
        .then(function(data){//is important that the data was returned as Aray Buffer
                console.log('Stream download complete, stop animation!');
                $scope.myBlobObject=new Blob([data],{ type:'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'});
        },function(fail){
                console.log('Download Error, stop animation and show error message');
                                    $scope.myBlobObject=[];
                                });
                            }; 

EN SERVICE:

function getStream(params){
                 console.log("RUNNING");
                 var deferred = $q.defer();

                 $http({
                     url:'../downloadURL/',
                     method:"PUT",//you can use also GET or POST
                     data:params,
                     headers:{'Content-type': 'application/json'},
                     responseType : 'arraybuffer',//THIS IS IMPORTANT
                    })
                    .success(function (data) {
                        console.debug("SUCCESS");
                        deferred.resolve(data);
                    }).error(function (data) {
                         console.error("ERROR");
                         deferred.reject(data);
                    });

                 return deferred.promise;
                };

BACKEND (sur PRINTEMPS):

@RequestMapping(value = "/downloadURL/", method = RequestMethod.PUT)
public void downloadExcel(HttpServletResponse response,
        @RequestBody Map<String,String> spParams
        ) throws IOException {
        OutputStream outStream=null;
outStream = response.getOutputStream();//is important manage the exceptions here
ObjectThatWritesOnOutputStream myWriter= new ObjectThatWritesOnOutputStream();// note that this object doesn exist on Java,
ObjectThatWritesOnOutputStream.write(outStream);//you can configure more things here
outStream.flush();
return;
}
4
havelino

Si vous avez accès à sur le serveur, définissez les en-têtes comme a répondu à cette question plus générale .

Content-Type: application/octet-stream
Content-Disposition: attachment;filename=\"filename.xxx\"

En lisant les commentaires sur cette réponse, il est conseillé d’utiliser un type de contenu plus spécifique que octet-stream.

4
plong0

Je ne voulais pas d'URL statique. J'ai AjaxFactory pour faire toutes les opérations ajax. Je reçois l'URL de l'usine et la lie comme suit.

<a target="_self" href="{{ file.downloadUrl + '/' + order.OrderId + '/' + fileName }}" download="{{fileName}}">{{fileName}}</a>

Merci @AhlemMustapha

2
om471987

Cela a fonctionné pour moi en angulaire:

var a = document.createElement("a");
a.href = 'fileURL';
a.download = 'fileName';
a.click();
0
Zohab Ali