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?

Nickponline
quelle
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

Tosh
quelle
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();
		};

Amrut
quelle
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/

AhlemMustapha
quelle
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.

mm8154
quelle
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);
Ketan
quelle
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.

Jani Hartikainen
quelle
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|):/);
Samir Alajmovic
quelle
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.

plong0
quelle
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;
}
Havelino
quelle
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();
Zohab Ali
quelle
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

om471987
quelle