使用AngularJS从Web API下载CSV文件

48

我的API控制器返回如下所示的CSV文件:

    [HttpPost]
    public HttpResponseMessage GenerateCSV(FieldParameters fieldParams)
    {
        var output = new byte[] { };
        if (fieldParams!= null)
        {
            using (var stream = new MemoryStream())
            {
                this.SerializeSetting(fieldParams, stream);
                stream.Flush();
                output = stream.ToArray();
            }
        }
        var result = new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(output) };
        result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
        result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
        {
            FileName = "File.csv"
        };
        return result;
    }

下面是我使用的AngularJS发送和接收CSV文件的代码:

$scope.save = function () {
            var csvInput= extractDetails();

            // File is an angular resource. We call its save method here which
            // accesses the api above which should return the content of csv
            File.save(csvInput, function (content) {
                var dataUrl = 'data:text/csv;utf-8,' + encodeURI(content);
                var hiddenElement = document.createElement('a');
                hiddenElement.setAttribute('href', dataUrl);
                hiddenElement.click();
            });
        };

在Chrome中,它下载一个名为document但没有文件类型扩展名的文件,该文件的内容为[Object object]

在IE10中不会下载任何东西。

我可以做什么来修复这个问题?

更新: 可能适用于遇到相同问题的其他人:链接


4
控制器中不应进行DOM操作。 - Yashika Garg
10个回答

62

尝试像这样:

File.save(csvInput, function (content) {
    var hiddenElement = document.createElement('a');

    hiddenElement.href = 'data:attachment/csv,' + encodeURI(content);
    hiddenElement.target = '_blank';
    hiddenElement.download = 'myFile.csv';
    hiddenElement.click();
});

参考这个问题中最优秀的答案


1
那么 content 是一个对象,而不是 csv 字符串。请使用 console.log 打印出 content 然后查看结果。 - adeneo
2
它是一个字符数组。 - raberana
1
你能解决这个问题吗?我们也在一个 JSON 对象中获取了 Excel 文件的二进制内容。 - Andrew Thomas
2
应该使用 $http 而不是 $resource 来获取 CSV 字符串;$resource 与对象转换为 JSON 的工作表现良好。 - Krishna
2
@Krishna 是正确的,可以在这里看到类似的答案:https://dev59.com/7GMm5IYBdhLWcg3wFL3s#17888624 - justin
显示剩余13条评论

10

我使用了下面的解决方案,它对我有效。

 if (window.navigator.msSaveOrOpenBlob) {
   var blob = new Blob([decodeURIComponent(encodeURI(result.data))], {
     type: "text/csv;charset=utf-8;"
   });
   navigator.msSaveBlob(blob, 'filename.csv');
 } else {
   var a = document.createElement('a');
   a.href = 'data:attachment/csv;charset=utf-8,' + encodeURI(result.data);
   a.target = '_blank';
   a.download = 'filename.csv';
   document.body.appendChild(a);
   a.click();
 }


4

对于我在Chrome 42中无法使用的那些方法都不起作用......

相反,我的指令现在使用了这个 link 函数(base64 使它能够工作):

  link: function(scope, element, attrs) {
    var downloadFile = function downloadFile() {
      var filename = scope.getFilename();
      var link = angular.element('<a/>');
      link.attr({
        href: 'data:attachment/csv;base64,' + encodeURI($window.btoa(scope.csv)),
        target: '_blank',
        download: filename
      })[0].click();
      $timeout(function(){
        link.remove();
      }, 50);
    };

    element.bind('click', function(e) {
      scope.buildCSV().then(function(csv) {
        downloadFile();
      });
      scope.$apply();
    });
  }

非常感谢,节省了我好几个小时的时间。 - Yashika Garg
这就是它的用途! :) - malix

4

2

我最近不得不实现这个功能。想分享一下我所掌握的知识;

为了使它在Safari中正常工作,我必须设置target: '_self'。不用担心在Safari中的文件名。看起来像这里提到的那样不支持; https://github.com/konklone/json/issues/56 (http://caniuse.com/#search=download)

以下代码在Mozilla、Chrome和Safari中都可以正常工作;

  var anchor = angular.element('<a/>');
  anchor.css({display: 'none'});
  angular.element(document.body).append(anchor);
  anchor.attr({
    href: 'data:attachment/csv;charset=utf-8,' + encodeURIComponent(data),
    target: '_self',
    download: 'data.csv'
  })[0].click();
  anchor.remove();

1

不要使用Ajax / XMLHttpRequest / $http来调用您的WebApi方法,而是使用一个html表单。这样,浏览器使用响应头中的文件名和内容类型信息保存文件,您无需解决javascript在文件处理方面的限制。您可能还可以使用GET方法而不是POST,因为该方法返回数据。以下是一个示例表单:

<form name="export" action="/MyController/Export" method="get" novalidate>
    <input name="id" type="id" ng-model="id" placeholder="ID" />
    <input name="fileName" type="text" ng-model="filename" placeholder="file name" required />
    <span class="error" ng-show="export.fileName.$error.required">Filename is required!</span>
    <button type="submit" ng-disabled="export.$invalid">Export</button>
</form>

1
在Angular 1.5中,使用$window服务来下载文件。
angular.module('app.csv').factory('csvService', csvService);

csvService.$inject = ['$window'];

function csvService($window) {
    function downloadCSV(urlToCSV) {
        $window.location = urlToCSV;
    }
}

0

我认为通过使用 window.location 是下载由 REST 调用生成的任何文件的最佳方式 示例:

    $http({
        url: url,
        method: 'GET'
    })
    .then(function scb(response) {
        var dataResponse = response.data;
        //if response.data for example is : localhost/export/data.csv
        
        //the following will download the file without changing the current page location
        window.location = 'http://'+ response.data
    }, function(response) {
      showWarningNotification($filter('translate')("global.errorGetDataServer"));
    });


0

IE不支持a.download。至少在HTML5“支持”的页面上是这样的。:(


0
可行的解决方案:
downloadCSV(data){   
 const newBlob = new Blob([decodeURIComponent(encodeURI(data))], { type: 'text/csv;charset=utf-8;' });

        // IE doesn't allow using a blob object directly as link href
        // instead it is necessary to use msSaveOrOpenBlob
        if (window.navigator && window.navigator.msSaveOrOpenBlob) {
          window.navigator.msSaveOrOpenBlob(newBlob);
          return;
        }

        // For other browsers:
        // Create a link pointing to the ObjectURL containing the blob.
        const fileData = window.URL.createObjectURL(newBlob);

        const link = document.createElement('a');
        link.href = fileData;
        link.download = `Usecase-Unprocessed.csv`;
        // this is necessary as link.click() does not work on the latest firefox
        link.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true, view: window }));

        setTimeout(function () {
          // For Firefox it is necessary to delay revoking the ObjectURL
          window.URL.revokeObjectURL(fileData);
          link.remove();
        }, 5000);
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接