W jaki sposób udostępniasz plik do pobrania za pomocą AngularJS lub Javascript?


96

Mam tekst w ukrytym obszarze tekstu. Po kliknięciu przycisku chciałbym, aby tekst był oferowany do pobrania jako .txtplik. Czy jest to możliwe przy użyciu AngularJS lub Javascript?


1
Jakie przeglądarki obsługujesz? Można to rozwiązać na kilka kreatywnych sposobów (np. Data-uris, bloby, interfejs API historii przeglądarki itp.), Ale to naprawdę zależy.
Benjamin Gruenbaum

Angular File Saver to dobry dodatek do mniej nowoczesnych przeglądarek.
georgeawg

Odpowiedzi:


110

Możesz zrobić coś takiego za pomocą Blob.

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

w kontrolerze:

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

aby włączyć adres URL:

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

Proszę to zanotować

Za każdym razem, gdy wywołujesz metodę createObjectURL (), tworzony jest nowy adres URL obiektu, nawet jeśli został już utworzony dla tego samego obiektu. Każdy z nich musi zostać zwolniony przez wywołanie URL.revokeObjectURL (), gdy nie są już potrzebne. Przeglądarki zwolnią je automatycznie po wyładowaniu dokumentu; jednak w celu uzyskania optymalnej wydajności i wykorzystania pamięci, jeśli są bezpieczne czasy, w których można je jawnie zwolnić, należy to zrobić.

Źródło: MDN


3
Nowoczesne przeglądarki i IE10 +
dave1010,

@thriqon wow firefox + chrome naprawdę pokazuje innych!
JonnyRaa

Świetne rozwiązanie, ale $scope.urlnie zadziałało. Musiałem window.locationzamiast tego użyć .
Gustavo Straube

7
Zauważyłem, że wtedy tag kotwicy jest poprzedzony przedrostkiem niebezpiecznym. Aby to obejść, musisz dodać „blob” do białej listy w pliku app.js, używając $ compileProvider `.config (['$ compileProvider', function ($ compileProvider) {$ compileProvider.aHrefSanitizationWhitelist (/ ^ \ s * (https? | ftp | mailto | tel | file | blob): /);} ` docs.angularjs.org/api/ng/provider/$compileProvider
coderman

10
Ten downloadatrybut nie jest jednak obsługiwany w żadnej wersji IE ani Safari caniuse.com/#feat=download
Aaron,

33

Wystarczy kliknąć przycisk, aby pobrać za pomocą następującego kodu.

w html

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

W kontrolerze

$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 pracował dla mnie w razie potrzeby, ale czy możesz wyjaśnić kod?
Harsh Daftary

Podoba mi się to rozwiązanie! Podczas pobierania danych z serwera, np. Przy użyciu $http.get(...), ustaw responseType:'arraybuffer'tak, jak wyjaśniono tutaj: stackoverflow.com/questions/21628378/ ...
Tim Büthe

Działa w Chrome (Win), ale Safari (Mac) po prostu otwiera plik blob w przeglądarce. (blob: https / ...) W ten sposób to rozwiązanie pozwala mi jednak czekać na spełnienie moich obietnic.
sekky

26

Spróbuj tego

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

i odwiedź tę stronę, może być dla Ciebie pomocna :)

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


7
Uważaj na downloadatrybut, który nadal nie jest obsługiwany przez żadną wersję IE ani Safari. Sprawdź to tutaj: caniuse.com/#feat=download
Pierre-Adrien Buisson

Kiedy używam go z kątowym, przenoszę adres URL do $ urlRouterProvider i przekierowuję na moją domyślną stronę, czy istnieje rozwiązanie umożliwiające pobranie pliku zamiast nawigacji
HardikDG

Zawsze uważam, że to protekcjonalne, gdy ktoś umieszcza taki link.
slugmandrew

22

Można to zrobić w javascript bez konieczności otwierania innego okna przeglądarki.

window.location.assign('url');

Zastąp „url” linkiem do pliku. Możesz umieścić to w funkcji i wywołać ją, ng-clickjeśli chcesz uruchomić pobieranie z przycisku.


2
Zamiast tego zastępuje witrynę dokumentem Pdf, aby wyświetlić okno dialogowe pobierania.
fdrv

Dzięki! Działa jak marzenie.
Sagi

14

W naszym obecnym projekcie w pracy mieliśmy niewidoczny element iFrame i musiałem podać adres URL pliku do elementu iFrame, aby uzyskać okno dialogowe pobierania. Po kliknięciu przycisku kontroler generuje dynamiczny adres URL i wyzwala zdarzenie $ scope, w którym plik niestandardowydirective znajduje się lista którą napisałem. Dyrektywa doda element iFrame do treści, jeśli jeszcze nie istnieje, i ustawi dla niego atrybut url.

EDYCJA: Dodanie dyrektywy

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

Ta dyrektywa odpowiada na zdarzenie kontrolera o nazwie downloadFile

więc w kontrolerze robisz

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

1
Fragment kodu byłby bardzo pomocny.
Sudhir N

Powyższa dyrektywa nie działa dla mnie, kiedy umieszczam tworzenie iframe poza zakresem. $
On

Tak, $ scope. $ Broadcast działa tylko na dzieci. Jeśli to możliwe, możesz umieścić dyrektywę w zakresie najwyższego poziomu.
Ketan

12

Można ustawić location.hrefdo danych URI zawierający dane, które chcesz pozwolić pobieranie użytkownika. Poza tym nie sądzę, aby można było to zrobić za pomocą samego JavaScript.


Działa to świetnie dla mnie i było znacznie czystsze niż wszystkie inne rzeczy, które próbowaliśmy, lub IMHO, skomplikowane podejścia zalecane powyżej. Angular całkowicie to ignoruje. Możesz też użyć window.open () / $ window.open (), jeśli chcesz spróbować otworzyć w innym oknie. Ale napotkasz programy blokujące wyskakujące okienka w nowoczesnych przeglądarkach ...
XML,

1
W Angular 1.3 $location.hrefzmieniono na$window.location.href
igortg

To świetna odpowiedź. Poniższy link w SO zawiera pełny działający przykład: stackoverflow.com/a/30889331/1625820
herrtim

7

Chciałbym tylko dodać, że w przypadku, gdy nie pobierze pliku z powodu niebezpiecznego: blob: null ... kiedy najedziesz kursorem na przycisk pobierania, musisz go wyczyścić. Na przykład,

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

app.config (function ($ compileProvider) {

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

5

Jeśli masz dostęp do na serwerze, rozważ ustawienie nagłówków zgodnie z odpowiedzią na to bardziej ogólne pytanie .

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

Czytając komentarze do tej odpowiedzi, zaleca się użycie bardziej szczegółowego typu treści niż strumień oktetu.


4

Miałem ten sam problem i spędziłem wiele godzin na szukaniu różnych rozwiązań, a teraz dołączam do wszystkich komentarzy w tym poście. Mam nadzieję, że będzie pomocna, moja odpowiedź została poprawnie przetestowana na Internet Explorerze 11, Chrome i FireFoxie.

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>

DYREKTYWA:

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

        }
    }
})

W KONTROLERZE:

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

CZYNNY:

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 (na WIOSNĘ):

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

Dzięki, to naprawdę zadziałało, szczególnie, że potrzebowałem dużych transferów plików.
Marcos Paulo SUS

3

To zadziałało dla mnie w kątowym:

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

Jeśli masz ciąg, który chcesz pobrać, po prostu zmień fileURL nadata:text/plain;base64,${btoa(theStringGoesHere)}
Chicken Soup

2

Nie chciałem statycznego adresu URL. Mam AjaxFactory do wykonywania wszystkich operacji Ajax. Pobieram adres URL z fabryki i wiążę go w następujący sposób.

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

Dzięki @AhlemMustapha

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.