57

I am trying to stream a csv file from a node.js server. The server portion is very simple :

server.get('/orders' function(req, res) {
  res.setHeader('content-type', 'text/csv');
  res.setHeader('content-disposition', 'attachment; filename='orders.csv');
  return orders.pipe(res); // assuming orders is a csv file readable stream (doesn't have to be a stream, can be a normal response)
}

In my angular controller I am trying to do something like this

$scope.csv = function() {
    $http({method: 'GET', url: '/orders'});
};

This function is called when there's a click on a button with ng-click in my view :

<button ng-click="csv()">.csv</button>

I have looked at other answers about downloading files from server in Angular, but didn't find anything that worked for me. Is there a common way to do this ? Seems like something that should be simple.

2
  • first I can see is that you url in $scope.csv isn't doesn't correspond with what you have in server.get Commented Jan 3, 2014 at 12:57
  • @dcodesmith ignore that please (fixed) was just for the question. I actually do see the request on the server Commented Jan 3, 2014 at 12:59

5 Answers 5

117

$http service returns a promise which has two callback methods as shown below.

$http({method: 'GET', url: '/someUrl'}).
  success(function(data, status, headers, config) {
     var anchor = angular.element('<a/>');
     anchor.attr({
         href: 'data:attachment/csv;charset=utf-8,' + encodeURI(data),
         target: '_blank',
         download: 'filename.csv'
     })[0].click();

  }).
  error(function(data, status, headers, config) {
    // handle error
  });
Sign up to request clarification or add additional context in comments.

17 Comments

That doesn't solve my problem since it just prints the file content to the console. I need to tell it somehow to let the browser handle the download...
It worked although in a kind of a hackish way: first chrome's popup blocker blocked the click, then when I allowed popups, it opened the download in a new window. Is there anyway to make it more pleasant ? if no, I guess it's good enough for me :)
On Mozilla Firefox is not working ...working fine in Chrome.
To get it work on Mozilla Firefox, attach your anchor to document: angular.element(document.body).append(anchor);
stackoverflow.com/a/35448277/3366706 works for IE 11 and chrome
|
21

Most of the references on the web about this issue point out to the fact that you cannot download files via ajax call 'out of the box'. I have seen (hackish) solutions that involve iframes and also solutions like @dcodesmith's that work and are perfectly viable.

Here's another solution I found that works in Angular and is very straighforward.

In the view, wrap the csv download button with <a> tag the following way :

<a target="_self" ng-href="{{csv_link}}">
  <button>download csv</button>
</a>

(Notice the target="_self there, it's crucial to disable Angular's routing inside the ng-app more about it here)

Inside youre controller you can define csv_link the following way :

$scope.csv_link = '/orders' + $window.location.search;

(the $window.location.search is optional and onlt if you want to pass additionaly search query to your server)

Now everytime you click the button, it should start downloading.

4 Comments

Is there any way to do this, if we need to post some big object to server & get the generated word/pdf file?
@ShivKumar yeah, in response to the POST request return the URL to the csv.
Can you set up the JSFiddle with full controller and UI code?
This solution is no different with having a simple <a/> tag with with href evaluated by angular.
21
var anchor = angular.element('<a/>');
anchor.css({display: 'none'}); // Make sure it's not visible
angular.element(document.body).append(anchor); // Attach to document

anchor.attr({
    href: 'data:attachment/csv;charset=utf-8,' + encodeURI(data),
    target: '_blank',
    download: 'filename.csv'
})[0].click();

anchor.remove(); // Clean it up afterwards

This code works both Mozilla and chrome

6 Comments

Thank you for this answer. This one does work in Chrome and Firefox.
Which version of each browser?
This is genius. The technique can be used to download arbitrary data from angular as a file.
what is IE version ?
works fine in firefox and chrome but not working in safari
|
7

This is what worked for me for IE 11+, Firefox and Chrome. In safari it downloads a file but as unknown and the filename is not set.

if (window.navigator.msSaveOrOpenBlob) {
    var blob = new Blob([csvDataString]);  //csv data string as an array.
    // IE hack; see http://msdn.microsoft.com/en-us/library/ie/hh779016.aspx
    window.navigator.msSaveBlob(blob, fileName);
} else {
    var anchor = angular.element('<a/>');
    anchor.css({display: 'none'}); // Make sure it's not visible
    angular.element(document.body).append(anchor); // Attach to document for FireFox

    anchor.attr({
        href: 'data:attachment/csv;charset=utf-8,' + encodeURI(csvDataString),
        target: '_blank',
        download: fileName
})[0].click();
anchor.remove();
}

Comments

0

Using angular 1.5.9

I made it working like this by setting the window.location to the csv file download url. Tested and its working with the latest version of Chrome and IE11.

Angular

   $scope.downloadStats = function downloadStats{
        var csvFileRoute = '/stats/download';
        $window.location = url;
    }

html

<a target="_self" ng-click="downloadStats()"><i class="fa fa-download"></i> CSV</a>

In php set the below headers for the response:

$headers = [
    'content-type'              => 'text/csv',
    'Content-Disposition'       => 'attachment; filename="export.csv"',
    'Cache-control'             => 'private, must-revalidate, post-check=0, pre-check=0',
    'Content-transfer-encoding' => 'binary',
    'Expires' => '0',
    'Pragma' => 'public',
];

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.