2013-05-13 59 views
87

我在隐藏的textarea中有一些文字。当点击一个按钮时,我想将提供的文本作为.txt文件提供下载。这可能使用AngularJS或Javascript吗?如何使用AngularJS或Javascript为文件下载?

+1

你支持哪些浏览器?这可以通过一些创造性的方式来解决(比如data-uris,blob,浏览器的历史API等),但这真的取决于。 – 2013-05-13 18:07:40

+0

[Angular File Saver](https://github.com/alferov/angular-file-saver)是较不现代的浏览器的良好补丁。 – georgeawg 2017-03-12 23:19:38

回答

98

您可以使用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(时间),一即使您已经创建了一个f,也会创建新的对象URL或同一个对象。当你不再需要它们时,每一个都必须通过调用URL.revokeObjectURL()来释放。当文档被卸载时,浏览器会自动释放这些文件;但是,为了获得最佳性能和内存使用情况,如果有明确卸载它们的安全时间,则应该这样做。

来源:MDN

+2

有趣的是,我不知道浏览器对此支持的是 – 2013-05-13 12:38:31

+17

http: //caniuse.com/bloburls – thriqon 2013-09-30 05:26:34

+2

现代浏览器和IE10 + – dave1010 2013-12-11 17:10:50

11

您可以将location.href设置为data URI,其中包含要让用户下载的数据。除此之外,我不认为有什么方法可以用JavaScript来完成。

+0

这对我来说很好,比我们尝试的所有其他事情更清洁,或者恕我直言,上面推荐的复杂方法。 Angular完全忽略它。或者,如果您想尝试在另一个窗口中打开,也可以使用window.open()/ $ window.open()。但是你会碰到现代浏览器中的弹出窗口拦截器...... – XML 2014-01-06 02:29:20

+1

在Angular 1.3中,'$ location.href'改为'$ window.location.href' – itghisi 2015-05-12 18:39:33

14

在我们当前的工作项目,我们有一个无形的iFrame,我不得不养活的URL文件的iFrame来获得一个下载对话框。点击按钮后,控制器将生成动态url并触发一个$ scope事件,其中列出了我编写的自定义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

代码片段将是一个很大的帮助。 – 2013-10-08 09:21:20

+0

添加了代码片段。 – Ketan 2013-10-08 19:14:22

+0

上面的指令不适用于我,当我将iframe创建为范围以外时,$创建iframe,但$ on事件在使用广播时未调用 – Kanagu 2013-12-17 07:31:02

6

只想补充一点的情况下,它不会因为不安全而下载文件:blob:null ...当您将鼠标悬停在下载按钮上时,必须对其进行清理。例如,

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

app。配置(函数($ compileProvider){

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

试试这个

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

和访问这个网站也可能对你有帮助:)

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

+0

作品像魅力。 Mucho gracias(y) – om471987 2014-05-05 21:56:04

+7

请注意任何IE或Safari版本都不支持的“下载”属性。检查它在这里:http://caniuse.com/#feat=download – 2014-11-14 11:18:22

+0

当我使用它与角度它采取URL到$ urlRouterProvider并重定向到我的默认页面,有没有任何解决方案下载文件,而不是导航 – HardikDG 2016-02-25 03:46:00

2

我没有想静态URL。我有用于执行所有ajax操作的AjaxFactory。我从工厂获取url并按如下方式进行绑定。

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

感谢@AhlemMustapha

19

这可以在JavaScript中完成,而无需打开另一个浏览器窗口。

window.location.assign('url'); 

将“url”替换为指向您文件的链接。你可以把它放在一个函数中,如果你需要触发一个按钮的下载,可以用ng-click来调用它。

+2

它用Pdf文档替换网站,而不是显示下载对话窗口。 – fdrv 2016-04-06 05:25:19

26

只需点击按钮使用下面的代码下载。

在HTML

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

在控制器

$scope.saveJSON = function() { 
 
\t \t \t $scope.toJSON = ''; 
 
\t \t \t $scope.toJSON = angular.toJson($scope.data); 
 
\t \t \t var blob = new Blob([$scope.toJSON], { type:"application/json;charset=utf-8;" }); \t \t \t 
 
\t \t \t var downloadLink = angular.element('<a></a>'); 
 
         downloadLink.attr('href',window.URL.createObjectURL(blob)); 
 
         downloadLink.attr('download', 'fileName.json'); 
 
\t \t \t downloadLink[0].click(); 
 
\t \t };

+1

这是如何在Safari + IE中使用的? – 2015-03-18 14:36:07

+1

@Amrut 根据需要为我工作,但你能解释代码吗? – 2015-07-29 08:29:53

+0

喜欢这个解决方案!当从服务器获取数据时,例如使用'$ http.get(...)'确保设置'responseType:'arraybuffer''就像这里解释的:http://stackoverflow.com/questions/21628378/angularjs-display-blob-pdf-in-an -angular-app – 2015-09-01 15:33:43

3

我有德同样的问题,花很多时间找迪不同的解决方案,现在我加入了这篇文章中的所有评论。我希望它会有帮助,我的答案在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 = /*@[email protected]*/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(春):

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

谢谢,这真的对我有用,特别是因为我需要大文件传输。 – 2017-02-08 15:56:05

3

如果你有机会到服务器上,考虑设置标头,answered in this more general question

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

阅读对该答案的评论,建议使用比八位组流更具体的内容类型。