Come servite un file per il download con AngularJS o Javascript?


96

Ho del testo in un'area di testo nascosta. Quando si fa clic su un pulsante, desidero che il testo venga offerto per il download come .txtfile. È possibile utilizzando AngularJS o Javascript?


1
Quali browser supportate? Questo può essere risolto in alcuni modi creativi (come data-uris, blob, API della cronologia del browser, ecc.) Ma dipende davvero.
Benjamin Gruenbaum

Angular File Saver è un buon polyfill per i browser meno moderni.
georgeawg

Risposte:


110

Puoi fare qualcosa di simile usando Blob.

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

nel tuo controller:

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

per abilitare l'URL:

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

Si prega di notare che

Ogni volta che chiamate createObjectURL (), viene creato un nuovo URL oggetto, anche se ne avete già creato uno per lo stesso oggetto. Ognuno di questi deve essere rilasciato chiamando URL.revokeObjectURL () quando non ne hai più bisogno. I browser li rilasceranno automaticamente quando il documento viene scaricato; tuttavia, per prestazioni e utilizzo della memoria ottimali, se ci sono momenti sicuri in cui è possibile scaricarli esplicitamente, è necessario farlo.

Fonte: MDN


3
Browser moderni e IE10 +
dave1010

@thriqon wow firefox + chrome che mostra davvero gli altri lassù!
JonnyRaa

Ottima soluzione, ma $scope.urlnon ha funzionato per me. Ho dovuto usare window.locationinvece.
Gustavo Straube

7
Ho notato che quindi il tag di ancoraggio è preceduto da unsafe. Per aggirare questo problema dovrai aggiungere 'blob' alla lista bianca nel tuo app.js, usando $ compileProvider `.config (['$ compileProvider', function ($ compileProvider) {$ compileProvider.aHrefSanitizationWhitelist (/ ^ \ s * (https? | ftp | mailto | tel | file | blob): /);} ` docs.angularjs.org/api/ng/provider/$compileProvider
coderman

10
L' downloadattributo non è supportato in nessuna versione di IE o Safari sebbene caniuse.com/#feat=download
Aaron

33

Basta fare clic sul pulsante per scaricare utilizzando il seguente codice.

in html

<a class="btn" ng-click="saveJSON()" ng-href="{{ url }}">Export to JSON</a>

Nel controller

$scope.saveJSON = function () {
			$scope.toJSON = '';
			$scope.toJSON = angular.toJson($scope.data);
			var blob = new Blob([$scope.toJSON], { type:"application/json;charset=utf-8;" });			
			var downloadLink = angular.element('<a></a>');
                        downloadLink.attr('href',window.URL.createObjectURL(blob));
                        downloadLink.attr('download', 'fileName.json');
			downloadLink[0].click();
		};


1
@ Amrut ha funzionato per me secondo necessità, ma puoi spiegare il codice?
Harsh Daftary

Come questa soluzione! Quando ottenere i dati dal server, ad esempio utilizzando $http.get(...)assicurarsi di impostare responseType:'arraybuffer'come spiegato qui: stackoverflow.com/questions/21628378/...
Tim Buthe

Funziona in Chrome (Win), ma Safari (Mac) apre semplicemente il file blob nel browser. (blob: https / ...) In questo modo, questa soluzione mi consente di attendere che le mie promesse vengano risolte.
sekky

26

Prova questo

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

e visita questo sito potrebbe esserti utile :)

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


7
Fai attenzione downloadall'attributo che non è ancora supportato da nessuna versione di IE o Safari. Dai
Pierre-Adrien Buisson

Quando lo uso con angular, richiede l'URL a $ urlRouterProvider e reindirizza alla mia pagina predefinita, esiste una soluzione per scaricare un file invece della navigazione
HardikDG

Trovo sempre paternalistico quando qualcuno pubblica un link del genere.
slugmandrew

22

Questo può essere fatto in javascript senza la necessità di aprire un'altra finestra del browser.

window.location.assign('url');

Sostituisci "url" con il link al tuo file. Puoi metterlo in una funzione e chiamarlo con ng-clickse devi attivare il download da un pulsante.


2
Sostituisce il sito con il documento Pdf invece per mostrare la finestra di dialogo di download.
fdrv

Grazie! Funziona come un fascino.
Sagi

14

Nel nostro progetto attuale al lavoro avevamo un iFrame invisibile e ho dovuto fornire l'URL del file all'iFrame per ottenere una finestra di dialogo per il download. Al clic del pulsante, il controller genera l'URL dinamico e attiva un evento $ scope in cui directiveviene elencato un custom che ho scritto. La direttiva aggiungerà un iFrame al corpo se non esiste già e imposta l'attributo url su di esso.

EDIT: aggiunta di una direttiva

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

Questa direttiva risponde a un evento del controller chiamato downloadFile

quindi nel tuo controller lo fai

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

1
Lo snippet di codice sarebbe di grande aiuto.
Sudhir N

La direttiva sopra non funziona per me, quando metto la creazione di iframe fuori dall'ambito. $ Su crea iframe ma $ on evento non chiama quando si utilizza la trasmissione
Kanagu

Sì $ scope. $ Broadcast funziona solo sui bambini. Se possibile, puoi inserire la direttiva nell'ambito di livello superiore.
Ketan

12

È possibile impostare location.hrefun URI di dati contenente i dati che si desidera consentire all'utente di scaricare. Oltre a questo, non credo che ci sia alcun modo per farlo solo con JavaScript.


Questo funziona alla grande per me ed è stato molto più pulito di tutte le altre cose che stavamo provando, o IMHO, gli approcci complicati raccomandati sopra. Angular lo ignora completamente. Oppure puoi anche usare window.open () / $ window.open () se vuoi provare ad aprire in un'altra finestra. Ma ti imbatterai in blocchi popup nei browser moderni ...
XML

1
In Angular 1.3, $location.hrefmodificato in$window.location.href
igortg

Questa è un'ottima risposta. Il seguente link sul SO fornisce un esempio di lavoro completo: stackoverflow.com/a/30889331/1625820
herrtim

7

Vorrei solo aggiungerlo nel caso in cui non scarica il file a causa di unsafe: blob: null ... quando passi il mouse sul pulsante di download, devi disinfettarlo. Per esempio,

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

app.config (function ($ compileProvider) {

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

5

Se hai accesso a sul server, considera l'impostazione delle intestazioni come risposta a questa domanda più generale .

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

Leggendo i commenti su quella risposta, è consigliabile utilizzare un Content-Type più specifico rispetto a octet-stream.


4

Ho avuto lo stesso problema e ho passato molte ore a trovare soluzioni diverse, e ora mi unisco a tutti i commenti in questo post. Spero possa esserti utile, la mia risposta è stata testata correttamente su Internet Explorer 11, Chrome e FireFox.

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>

DIRETTIVA:

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

        }
    }
})

NEL CONTROLLORE:

$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=[];
                                });
                            }; 

IN SERVIZIO:

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 (su SPRING):

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

Grazie, questo ha funzionato davvero per me, specialmente perché avevo bisogno di trasferimenti di file di grandi dimensioni.
Marcos Paulo SUS

3

Questo ha funzionato per me in angolare:

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

Se desideri scaricare una stringa, cambia semplicemente fileURL indata:text/plain;base64,${btoa(theStringGoesHere)}
Chicken Soup

2

Non volevo Url statico. Ho AjaxFactory per fare tutte le operazioni ajax. Ricevo l'URL dalla fabbrica e lo associo come segue.

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

Grazie @AhlemMustapha

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.