Download text/csv content as files from server in Angular
Asked Answered
P

5

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.

Premonitory answered 3/1, 2014 at 12:54 Comment(2)
first I can see is that you url in $scope.csv isn't doesn't correspond with what you have in server.getTautog
@Tautog ignore that please (fixed) was just for the question. I actually do see the request on the serverPremonitory
T
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
  });
Tautog answered 3/1, 2014 at 13:6 Comment(17)
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...Premonitory
have a look at this #20301047Tautog
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 :)Premonitory
@Premonitory that should work. Let me know if it does plsTautog
very nice, thanks! I have found another solution to the problem (posted the answer). I'm accepting yours since you were first :)Premonitory
Hmm, no one's had this so far not that I'm aware of anyway. When you "download" is it a file or what?Tautog
What i've done is far easier, in my button method, instead of performing a request and handling successes, elements, etc. I just used it to form the full url, then used window.location = myUrl; if the url points to a downloadable file, it will download, but remember that you have to configure your server so even if a file type is to be displayed in a browser, it snaps to download.Oro
Great solution, solved my problem with downloading a file through AngularJS and C# web API !Rotow
This works great for small files. However, for files larger than 1Mb, the download is not triggered in the browser (but devtools shows that the request contains the data). Any idea why this happens and how to solve this ?Gall
On Mozilla Firefox is not working ...working fine in Chrome.Yarn
Great answer I just did a copy paste && it worked as a charm!Kirwan
@fbtb, try replacing charset=utf-8 with base64 Tautog
To get it work on Mozilla Firefox, attach your anchor to document: angular.element(document.body).append(anchor);Phineas
This works just fine for me. However, it saves the file without prompting for a name, which would have been nice. Plus it automatically opens the file in Excel, but that might just be the way that I have Windows configuredGuerrilla
Unfortunately this doesn't work when I put it behind an nginx front-end proxy. Otherwise, it works fine.Essonite
https://mcmap.net/q/247180/-download-text-csv-content-as-files-from-server-in-angular works for IE 11 and chromeSprinkling
#20301047Cord
P
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.

Premonitory answered 3/1, 2014 at 15:0 Comment(4)
Is there any way to do this, if we need to post some big object to server & get the generated word/pdf file?Gentilis
@ShivKumar yeah, in response to the POST request return the URL to the csv.Learning
Can you set up the JSFiddle with full controller and UI code?Esra
This solution is no different with having a simple <a/> tag with with href evaluated by angular.Submarine
T
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

Tolan answered 7/8, 2015 at 7:1 Comment(6)
Thank you for this answer. This one does work in Chrome and Firefox.Weirdo
Which version of each browser?Piacular
This is genius. The technique can be used to download arbitrary data from angular as a file.Celie
what is IE version ?Tolan
works fine in firefox and chrome but not working in safariBald
Works fine, then I put it behind an nginx front-end proxy and I get a network error.Essonite
D
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();
}
Dictaphone answered 17/2, 2016 at 5:0 Comment(0)
M
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',
];
Minify answered 23/1, 2017 at 17:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.