Wie können Sie eine Datei mit AngularJS oder Javascript zum Download bereitstellen?


96

Ich habe Text in einem versteckten Textbereich. Wenn auf eine Schaltfläche geklickt wird, möchte ich den Text als .txtDatei zum Download anbieten . Ist dies mit AngularJS oder Javascript möglich?


1
Welche Browser unterstützen Sie? Dies kann auf einige kreative Arten gelöst werden (wie Daten-Uris, Blobs, die Verlaufs-API des Browsers usw.), aber das hängt wirklich davon ab.
Benjamin Gruenbaum

Angular File Saver ist eine gute Polyfüllung für weniger moderne Browser.
Georgiaeawg

Antworten:


110

Sie können so etwas mit tun Blob.

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

in Ihrem Controller:

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

um die URL zu aktivieren:

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

Bitte beachte, dass

Jedes Mal, wenn Sie createObjectURL () aufrufen, wird eine neue Objekt-URL erstellt, auch wenn Sie bereits eine für dasselbe Objekt erstellt haben. Jedes dieser Elemente muss durch Aufrufen von URL.revokeObjectURL () freigegeben werden, wenn Sie sie nicht mehr benötigen. Browser geben diese automatisch frei, wenn das Dokument entladen wird. Für eine optimale Leistung und Speichernutzung sollten Sie dies jedoch tun, wenn es sichere Zeiten gibt, in denen Sie diese explizit entladen können.

Quelle: MDN


3
Moderne Browser & IE10 +
dave1010

@ Thriqon Wow Firefox + Chrom zeigt wirklich die anderen dort oben!
JonnyRaa

Tolle Lösung, hat aber $scope.urlbei mir nicht funktioniert. Ich musste window.locationstattdessen verwenden.
Gustavo Straube

7
Mir ist aufgefallen, dass dem Ankertag dann ein unsicheres Präfix vorangestellt wird. Um dies zu umgehen, müssen Sie der weißen Liste in Ihrer app.js 'blob' hinzufügen, indem Sie $ compileProvider `.config (['$ compileProvider', Funktion ($ compileProvider) {$ compileProvider.aHrefSanitizationWhitelist (/ ^) verwenden \ s * (https? | ftp | mailto | tel | file | blob): /);} ` docs.angularjs.org/api/ng/provider/$compileProvider
coderman

10
Das downloadAttribut wird in keiner IE-
Aaron

33

Klicken Sie einfach auf die Schaltfläche, um den folgenden Code herunterzuladen.

in html

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

In der Steuerung

$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 hat bei Bedarf für mich gearbeitet, aber können Sie den Code erklären?
Harte Daftary

Wie diese Lösung! Wenn Sie die Daten vom Server abrufen , z. B. mit, $http.get(...)stellen Sie sicher, dass Sie die responseType:'arraybuffer'hier erläuterten Einstellungen vornehmen
Tim Büthe

Funktioniert in Chrome (Win), aber Safari (Mac) öffnet nur die Blobbed-Datei im Browser. (blob: https / ...) Auf diese Weise kann ich mit dieser Lösung warten, bis meine Versprechen erfüllt sind.
Sekky

26

Versuche dies

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

und besuchen Sie diese Seite, es könnte für Sie hilfreich sein :)

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


7
Achten Sie auf das downloadAttribut, das von keiner IE- oder Safari-Version unterstützt wird. Überprüfen Sie es hier: caniuse.com/#feat=download
Pierre-Adrien Buisson

Wenn ich es mit eckigen verwende, nimmt es URL zu $ ​​urlRouterProvider und leitet zu meiner Standardseite um. Gibt es eine Lösung, um eine Datei anstelle der Navigation herunterzuladen
HardikDG

Ich finde es immer bevormundend, wenn jemand einen solchen Link veröffentlicht.
Slugmandrew

22

Dies kann in Javascript erfolgen, ohne dass ein anderes Browserfenster geöffnet werden muss.

window.location.assign('url');

Ersetzen Sie 'url' durch den Link zu Ihrer Datei. Sie können dies in eine Funktion einfügen und mit aufrufen, ng-clickwenn Sie den Download über eine Schaltfläche auslösen müssen.


2
Stattdessen wird die Site durch ein PDF-Dokument ersetzt, um das Download-Dialogfenster anzuzeigen.
fdrv

Vielen Dank! Klappt wunderbar.
Sagi

14

In unserem aktuellen Projekt hatten wir einen unsichtbaren iFrame und ich musste die URL für die Datei dem iFrame zuführen, um ein Download-Dialogfeld zu erhalten. Beim Klicken auf die Schaltfläche generiert der Controller die dynamische URL und löst ein $ scope-Ereignis aus, in dem ein von directivemir geschriebener Benutzer aufgelistet wird. Die Direktive hängt einen iFrame an den Body an, falls dieser noch nicht vorhanden ist, und legt das URL-Attribut darauf fest.

EDIT: Hinzufügen einer Direktive

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

Diese Anweisung reagiert auf ein aufgerufenes Controller-Ereignis downloadFile

So tun Sie es in Ihrem Controller

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

1
Code-Snippet wäre eine große Hilfe.
Sudhir N

Die obige Direktive funktioniert nicht für mich, wenn ich die Iframe-Erstellung außerhalb des Gültigkeitsbereichs platziere. $ On erstellt Iframe, aber $ on-Ereignis ruft bei Verwendung von Broadcast nicht auf
Kanagu

Ja $ scope. $ Broadcast funktioniert nur bei Kindern. Sie können die Direktive nach Möglichkeit in den Bereich der obersten Ebene stellen.
Ketan

12

Sie können location.hrefeinen Daten-URI festlegen , der die Daten enthält, die der Benutzer herunterladen möchte. Abgesehen davon glaube ich nicht, dass es eine Möglichkeit gibt, dies nur mit JavaScript zu tun.


Dies funktioniert hervorragend für mich und war viel sauberer als alle anderen Dinge, die wir versucht haben, oder meiner Meinung nach die oben empfohlenen komplizierten Ansätze. Angular ignoriert es völlig. Sie können auch window.open () / $ window.open () verwenden, wenn Sie versuchen möchten, in einem anderen Fenster zu öffnen. Aber Sie werden in modernen Browsern auf Popup-Blocker stoßen ...
XML

1
In Angular 1.3, $location.hrefgeändert zu$window.location.href
igortg

Dies ist eine großartige Antwort. Der folgende Link auf SO bietet ein voll funktionsfähiges Beispiel: stackoverflow.com/a/30889331/1625820
herrtim

7

Ich möchte nur hinzufügen, dass die Datei nicht heruntergeladen wird, weil sie unsicher ist: blob: null ... Wenn Sie mit der Maus über den Download-Button fahren, müssen Sie sie bereinigen. Zum Beispiel,

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

app.config (Funktion ($ compileProvider) {

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

5

Wenn Sie auf dem Server Zugriff haben, sollten Sie die in dieser allgemeineren Frage beantworteten Header festlegen .

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

Wenn Sie die Kommentare zu dieser Antwort lesen, ist es ratsam, einen spezifischeren Inhaltstyp als den Oktett-Stream zu verwenden.


4

Ich hatte das gleiche Problem und verbrachte viele Stunden damit, verschiedene Lösungen zu finden, und jetzt verbinde ich alle Kommentare in diesem Beitrag. Ich hoffe, es wird hilfreich sein, meine Antwort wurde in Internet Explorer 11, Chrome und FireFox korrekt getestet.

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>

RICHTLINIE:

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

        }
    }
})

IM CONTROLLER:

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

IM DIENST:

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 (am FRÜHLING):

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

Danke, das hat bei mir wirklich funktioniert, zumal ich große Dateiübertragungen brauchte.
Marcos Paulo SUS

3

Das hat bei mir im Winkel funktioniert:

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

Wenn Sie eine Zeichenfolge haben, die Sie herunterladen möchten, ändern Sie einfach fileURL indata:text/plain;base64,${btoa(theStringGoesHere)}
Hühnersuppe

2

Ich wollte keine statische URL. Ich habe AjaxFactory für alle Ajax-Operationen. Ich erhalte eine URL aus der Fabrik und binde sie wie folgt.

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

Danke @AhlemMustapha

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.