คุณให้บริการไฟล์สำหรับดาวน์โหลดด้วย AngularJS หรือ Javascript ได้อย่างไร


97

ฉันมีข้อความบางส่วนในพื้นที่ข้อความที่ซ่อนอยู่ เมื่อคลิกปุ่มฉันต้องการให้ข้อความที่เสนอให้ดาวน์โหลดเป็น.txtไฟล์ เป็นไปได้โดยใช้ AngularJS หรือ Javascript?


1
คุณรองรับเบราว์เซอร์ใดบ้าง สิ่งนี้สามารถแก้ไขได้ด้วยวิธีที่สร้างสรรค์ (เช่น data-uris, blobs, API ประวัติของเบราว์เซอร์ ฯลฯ ) แต่ก็ขึ้นอยู่กับ
Benjamin Gruenbaum

Angular File Saverเป็น polyfill ที่ดีสำหรับเบราว์เซอร์ที่ทันสมัยน้อยกว่า
georgeawg

คำตอบ:


111

Blobคุณสามารถทำอะไรเช่นนี้โดยใช้

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

ในตัวควบคุมของคุณ:

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

เพื่อเปิดใช้งาน URL:

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

โปรดทราบว่า

ทุกครั้งที่คุณเรียก createObjectURL () URL ของออบเจ็กต์ใหม่จะถูกสร้างขึ้นแม้ว่าคุณจะสร้างไว้แล้วสำหรับออบเจ็กต์เดียวกันก็ตาม สิ่งเหล่านี้จะต้องถูกปล่อยออกมาโดยเรียก URL.revokeObjectURL () เมื่อคุณไม่ต้องการอีกต่อไป เบราว์เซอร์จะปล่อยสิ่งเหล่านี้โดยอัตโนมัติเมื่อเอกสารถูกยกเลิกการโหลด อย่างไรก็ตามเพื่อประสิทธิภาพและการใช้งานหน่วยความจำที่ดีที่สุดหากมีช่วงเวลาที่ปลอดภัยที่คุณสามารถยกเลิกการโหลดได้อย่างชัดเจนคุณควรทำเช่นนั้น

ที่มา: MDN


3
เบราว์เซอร์ที่ทันสมัยและ IE10 +
dave1010

@thriqon ว้าว firefox + chrome แสดงให้คนอื่นเห็นจริงๆ!
JonnyRaa

ทางออกที่ดี แต่$scope.urlไม่ได้ผลสำหรับฉัน ฉันต้องใช้window.locationแทน
Gustavo Straube

7
ฉันสังเกตว่าแท็กจุดยึดนั้นขึ้นต้นด้วยไม่ปลอดภัย ในการหลีกเลี่ยงสิ่งนั้นคุณจะต้องเพิ่ม 'blob' ในรายการสีขาวใน app.js ของคุณโดยใช้ $ compileProvider ".config (['$ compileProvider', function ($ compileProvider) {$ compileProvider.aHrefSanitizationWhitelist (/ ^ \ s * (https? | ftp | mailto | tel | file | blob): /);} ` docs.angularjs.org/api/ng/provider/$compileProvider
coderman

10
downloadแอตทริบิวต์ไม่สนับสนุนใน IE หรือ Safari รุ่นแม้ว่าcaniuse.com/#feat=download
แอรอน

33

เพียงคลิกปุ่มเพื่อดาวน์โหลดโดยใช้รหัสต่อไปนี้

ใน html

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

ในตัวควบคุม

$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 ทำงานให้ฉันตามต้องการ แต่คุณช่วยอธิบายรหัสได้ไหม
Harsh Daftary

ชอบวิธีนี้! เมื่อได้รับข้อมูลจากเซิร์ฟเวอร์เช่นใช้$http.get(...)make sure to set responseType:'arraybuffer'ดังที่อธิบายไว้ที่นี่: stackoverflow.com/questions/21628378/…
Tim Büthe

ใช้งานได้ใน Chrome (Win) แต่ Safari (Mac) เพียงแค่เปิดไฟล์หยดในเบราว์เซอร์ (blob: https / ... ) เช่นเดียวกับที่วิธีนี้ทำให้ฉันรอให้คำสัญญาของฉันได้รับการแก้ไข
sekky

26

ลองทำตามนี้

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

และเยี่ยมชมเว็บไซต์นี้อาจเป็นประโยชน์สำหรับคุณ :)

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


7
ระวังdownloadแอตทริบิวต์ที่ยังไม่รองรับใน IE หรือ Safari เวอร์ชันใด ๆ ตรวจสอบได้ที่นี่: caniuse.com/#feat=download
Pierre-Adrien Buisson

เมื่อฉันใช้กับ angular มันจะใช้ url เป็น $ urlRouterProvider และเปลี่ยนเส้นทางไปยังหน้าเริ่มต้นของฉันมีวิธีใดในการดาวน์โหลดไฟล์แทนการนำทางหรือไม่
HardikDG

ฉันมักจะพบว่ามันให้การสนับสนุนเมื่อมีคนโพสต์ลิงก์แบบนั้น
slugmandrew

23

สามารถทำได้ในจาวาสคริปต์โดยไม่จำเป็นต้องเปิดหน้าต่างเบราว์เซอร์อื่น

window.location.assign('url');

แทนที่ "url" ด้วยลิงก์ไปยังไฟล์ของคุณ คุณสามารถใส่ฟังก์ชันนี้และเรียกใช้งานได้ng-clickหากต้องการเรียกใช้การดาวน์โหลดจากปุ่ม


2
มันแทนที่ไซต์ด้วยเอกสาร Pdf แทนเพื่อแสดงหน้าต่างโต้ตอบการดาวน์โหลด
fdrv

ขอบคุณ! ใช้งานได้เหมือนมีเสน่ห์
Sagi

14

ในโครงการปัจจุบันของเราที่ทำงานเรามี iFrame ที่มองไม่เห็นและฉันต้องป้อน url สำหรับไฟล์ไปที่ iFrame เพื่อรับกล่องโต้ตอบการดาวน์โหลด เมื่อคลิกปุ่มตัวควบคุมจะสร้าง URL แบบไดนามิกและทริกเกอร์เหตุการณ์ $ ขอบเขตที่directiveฉันเขียนเองกำลังแสดงรายการ คำสั่งจะต่อท้าย iFrame เข้ากับเนื้อหาหากไม่มีอยู่แล้วและตั้งค่าแอตทริบิวต์ url ไว้

แก้ไข: การเพิ่มคำสั่ง

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

คำสั่งนี้ตอบสนองต่อเหตุการณ์คอนโทรลเลอร์ที่เรียกว่า downloadFile

ในคอนโทรลเลอร์ของคุณที่คุณทำ

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

1
ข้อมูลโค้ดจะช่วยได้มาก
Sudhir N

คำสั่งข้างต้นใช้ไม่ได้สำหรับฉันเมื่อฉันวางการสร้าง iframe นอกขอบเขต $ บนมันสร้าง iframe แต่ $ on event ไม่โทรเมื่อใช้การออกอากาศ
Kanagu

ใช่ $ ขอบเขตการออกอากาศ $ ใช้งานได้กับเด็กเท่านั้น คุณสามารถวางคำสั่งไว้ในขอบเขตระดับบนสุดได้หากทำได้
Ketan

12

คุณสามารถตั้งค่าlocation.hrefเป็นURI ข้อมูลที่มีข้อมูลที่คุณต้องการให้ผู้ใช้ดาวน์โหลด นอกจากนี้ฉันไม่คิดว่าจะมีวิธีใดที่ทำได้ด้วย JavaScript เพียงอย่างเดียว


วิธีนี้ใช้ได้ผลดีสำหรับฉันและสะอาดกว่าสิ่งอื่น ๆ ทั้งหมดที่เราพยายามหรือ IMHO ซึ่งเป็นแนวทางที่ซับซ้อนที่แนะนำข้างต้น Angular ละเว้นมันทั้งหมด หรือจะใช้ window.open () / $ window.open () ก็ได้ถ้าอยากลองเปิดในหน้าต่างอื่น แต่คุณจะพบกับตัวบล็อกป๊อปอัปในเบราว์เซอร์สมัยใหม่ ...
XML

1
ใน Angular 1.3 $location.hrefเปลี่ยนเป็น$window.location.href
igortg

นี่คือคำตอบที่ยอดเยี่ยม ลิงก์ต่อไปนี้ใน SO แสดงตัวอย่างการทำงานแบบเต็ม: stackoverflow.com/a/30889331/1625820
herrtim

7

อยากจะเพิ่มในกรณีที่ไม่ดาวน์โหลดไฟล์เนื่องจากไม่ปลอดภัย: blob: null ... เมื่อคุณวางเมาส์เหนือปุ่มดาวน์โหลดคุณต้องล้างมัน ตัวอย่างเช่น

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

app.config (ฟังก์ชัน ($ compileProvider) {

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

5

หากคุณสามารถเข้าถึงบนเซิร์ฟเวอร์ได้ให้พิจารณาตั้งค่าส่วนหัวตามคำตอบในคำถามทั่วไปนี้

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

เมื่ออ่านความคิดเห็นเกี่ยวกับคำตอบนั้นขอแนะนำให้ใช้ Content-Type ที่เฉพาะเจาะจงมากกว่า octet-stream


4

ฉันมีปัญหาเดียวกันและใช้เวลาหลายชั่วโมงในการค้นหาวิธีแก้ปัญหาที่แตกต่างกันและตอนนี้ฉันเข้าร่วมความคิดเห็นทั้งหมดในโพสต์นี้ฉันหวังว่ามันจะเป็นประโยชน์คำตอบของฉันได้รับการทดสอบอย่างถูกต้องบน Internet Explorer 11, Chrome และ 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>

ทิศทาง:

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

        }
    }
})

ในการควบคุม:

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

อยู่ในการให้บริการ:

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 (บน 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;
}

ขอบคุณสิ่งนี้ใช้ได้ผลกับฉันมากโดยเฉพาะเนื่องจากฉันต้องการการถ่ายโอนไฟล์ขนาดใหญ่
Marcos Paulo SUS

3

สิ่งนี้ใช้ได้ผลสำหรับฉันในเชิงมุม:

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

หากคุณมีสตริงที่ต้องการดาวน์โหลดเพียงแค่เปลี่ยน fileURL เป็นdata:text/plain;base64,${btoa(theStringGoesHere)}
Chicken Suop

2

ฉันไม่ต้องการ Static Url ฉันมี AjaxFactory สำหรับการดำเนินการ ajax ทั้งหมด ฉันได้รับ url จากโรงงานและผูกไว้ดังนี้

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

ขอบคุณ @AhlemMustapha

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.