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?

nickponline
źródło
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

Bzdury
źródło
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();
		};

Amrut
źródło
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/

AhlemMustapha
źródło
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.

mm8154
źródło
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);
Ketan
źródło
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.

Jani Hartikainen
źródło
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|):/);
Samir Alajmovic
źródło
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.

plong0
źródło
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;
}
Havelino
źródło
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();
Zohab Ali
źródło
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

om471987
źródło