Bagaimana Anda melayani file untuk diunduh dengan AngularJS atau Javascript?

96

Saya memiliki beberapa teks di textarea tersembunyi. Ketika sebuah tombol diklik, saya ingin teks tersebut ditawarkan untuk diunduh sebagai .txtfile. Apakah ini mungkin menggunakan AngularJS atau Javascript?

nickponline
sumber
1
Browser apa yang Anda dukung? Ini dapat diselesaikan dengan beberapa cara kreatif (seperti data-uris, blob, API riwayat browser, dll) tetapi itu sangat bergantung.
Benjamin Gruenbaum
Angular File Saver adalah polyfill yang bagus untuk browser yang kurang modern.
georgeawg

Jawaban:

110

Anda dapat melakukan hal seperti ini dengan menggunakan Blob.

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

di pengontrol Anda:

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

untuk mengaktifkan URL:

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

Harap dicatat bahwa

Setiap kali Anda memanggil createObjectURL (), URL objek baru dibuat, meskipun Anda sudah membuatnya untuk objek yang sama. Masing-masing harus dilepaskan dengan memanggil URL.revokeObjectURL () saat Anda tidak lagi membutuhkannya. Browser akan merilisnya secara otomatis saat dokumen dibongkar; namun, untuk kinerja dan penggunaan memori yang optimal, jika ada waktu aman saat Anda dapat membongkarnya secara eksplisit, Anda harus melakukannya.

Sumber: MDN

Omong kosong
sumber
3
Browser modern & IE10 +
dave1010
@thriqon wow firefox + chrome benar-benar menampilkan yang lain di sana!
JonnyRaa
Solusi hebat, tetapi $scope.urltidak berhasil untuk saya. Saya harus menggunakan window.locationsebagai gantinya.
Gustavo Straube
7
Saya perhatikan bahwa kemudian tag anchor diawali dengan unsafe. Untuk menyiasati itu, Anda perlu menambahkan 'blob' ke daftar putih di app.js, menggunakan $ compileProvider `.config (['$ compileProvider', function ($ compileProvider) {$ compileProvider.aHrefSanitizationWhitelist (/ ^ \ s * (https? | ftp | mailto | tel | file | blob): /);} ` docs.angularjs.org/api/ng/provider/$compileProvider
coderman
10
The downloadatribut tidak didukung dalam versi IE atau Safari meskipun caniuse.com/#feat=download
Aaron
33

Cukup klik tombol untuk mengunduh menggunakan kode berikut.

dalam html

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

Dalam pengontrol

$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
sumber
1
@ Amrut bekerja untuk saya sesuai kebutuhan, tetapi dapatkah Anda menjelaskan kodenya?
Harsh Daftary
Suka solusi ini! Saat mendapatkan data dari server, misalnya menggunakan $http.get(...)pastikan untuk mengatur responseType:'arraybuffer'seperti yang dijelaskan di sini: stackoverflow.com/questions/21628378/…
Tim Büthe
Berfungsi di Chrome (Win), tetapi Safari (Mac) hanya membuka file blobbed di browser. (blob: https / ...) Seperti itu solusi ini memungkinkan saya menunggu janji saya diselesaikan.
sekky
26

Coba ini

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

dan kunjungi situs ini, ini bisa membantu Anda :)

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

AhlemMustapha
sumber
7
Waspadai downloadatribut yang masih belum didukung oleh versi IE atau Safari. Lihat di sini: caniuse.com/#feat=download
Pierre-Adrien Buisson
Ketika saya menggunakannya dengan sudut, itu mengambil url ke $ urlRouterProvider dan mengarahkan ke halaman default saya, apakah ada solusi untuk mengunduh file daripada navigasi
HardikDG
Saya selalu merasa menggurui ketika seseorang memposting tautan seperti itu.
slugmandrew
22

Ini dapat dilakukan dalam javascript tanpa perlu membuka jendela browser lain.

window.location.assign('url');

Ganti 'url' dengan tautan ke file Anda. Anda dapat menempatkan ini dalam sebuah fungsi dan memanggilnya dengan ng-clickjika Anda perlu memicu unduhan dari sebuah tombol.

mm8154
sumber
2
Ini menggantikan situs dengan dokumen Pdf sebagai gantinya untuk menampilkan jendela dialog unduhan.
fdrv
Terima kasih! Bekerja seperti pesona.
Sagi
14

Dalam proyek kami saat ini di tempat kerja kami memiliki iFrame yang tidak terlihat dan saya harus memberi makan url untuk file tersebut ke iFrame untuk mendapatkan kotak dialog unduhan. Pada klik tombol, pengontrol menghasilkan url dinamis dan memicu peristiwa $ scope di mana kebiasaan yang directivesaya tulis, terdaftar. Direktif akan menambahkan iFrame ke badan jika belum ada dan menyetel atribut url di atasnya.

EDIT: Menambahkan perintah

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

Direktif ini menanggapi peristiwa pengontrol yang disebut downloadFile

jadi di pengontrol Anda, Anda melakukannya

$scope.$broadcast("downloadFile", url);
Ketan
sumber
1
Potongan kode akan sangat membantu.
Sudhir N
Arahan di atas tidak berfungsi untuk saya, ketika saya meletakkan pembuatan iframe di luar cakupan. $ Di atasnya membuat iframe tetapi $ pada acara tidak menelepon saat menggunakan siaran
Kanagu
Ya $ scope. $ Broadcast hanya berfungsi untuk anak-anak. Anda dapat meletakkan arahan pada cakupan tingkat atas jika memungkinkan.
Ketan
12

Anda dapat menyetel location.hrefke URI data yang berisi data yang Anda inginkan agar dapat diunduh pengguna. Selain itu, menurut saya tidak ada cara untuk melakukannya hanya dengan JavaScript.

Jani Hartikainen
sumber
Ini bekerja sangat baik untuk saya, dan jauh lebih bersih daripada semua hal lain yang kami coba, atau IMHO, pendekatan rumit yang direkomendasikan di atas. Angular mengabaikannya sepenuhnya. Atau, Anda juga dapat menggunakan window.open () / $ window.open () jika Anda ingin mencoba membuka di jendela lain. Tetapi Anda akan menemukan pemblokir popup di peramban modern ...
XML
1
Di Angular 1.3, $location.hrefdiubah menjadi$window.location.href
igortg
Ini jawaban yang bagus. Tautan berikut di SO memberikan contoh yang berfungsi penuh: stackoverflow.com/a/30889331/1625820
herrtim
7

Hanya ingin menambahkannya jika tidak mengunduh file karena tidak aman: blob: null ... saat Anda mengarahkan kursor ke tombol unduh, Anda harus membersihkannya. Misalnya,

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

app.config (function ($ compileProvider) {

$compileProvider.aHrefSanitizationWhitelist(/^\s*(|blob|):/);
Samir Alajmovic
sumber
5

Jika Anda memiliki akses ke di server, pertimbangkan untuk menyetel header seperti yang dijawab dalam pertanyaan yang lebih umum ini .

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

Membaca komentar tentang jawaban itu, disarankan untuk menggunakan Jenis Konten yang lebih spesifik daripada aliran oktet.

plong0
sumber
4

Saya memiliki masalah yang sama dan menghabiskan berjam-jam menemukan solusi yang berbeda, dan sekarang saya bergabung dengan semua komentar di posting ini. Saya harap ini, akan membantu, jawaban saya telah diuji dengan benar di Internet Explorer 11, Chrome dan 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>

PENGARAHAN :

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

        }
    }
})

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

DALAM PELAYANAN:

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 (pada 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;
}
havelino
sumber
Terima kasih, ini benar-benar berhasil untuk saya, terutama karena saya membutuhkan transfer file yang besar.
Marcos Paulo SUS
3

Ini bekerja untuk saya dalam sudut:

var a = document.createElement("a");
a.href = 'fileURL';
a.download = 'fileName';
a.click();
Zohab Ali
sumber
Jika Anda memiliki string yang ingin Anda unduh, cukup ubah fileURL menjadidata:text/plain;base64,${btoa(theStringGoesHere)}
Chicken Soup
2

Saya tidak ingin Static Url. Saya memiliki AjaxFactory untuk melakukan semua operasi ajax. Saya mendapatkan url dari pabrik dan mengikatnya sebagai berikut.

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

Terima kasih @AhlemMustapha

om471987
sumber