Reputation: 11816
my API controller is returning a csv file as seen below:
[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;
}
and my angularjs that will send and receive the csv file is shown below:
$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();
});
};
In chrome, it downloads a file which is called document
but has no file type extension.
The content of the file is [Object object]
.
In IE10, nothing is downloaded.
What could i do to fix this?
UPDATE: This might work for you guys out there with the same problem: link
Upvotes: 48
Views: 112089
Reputation: 810
Workable solution:
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);
}
Upvotes: 0
Reputation: 354
I think the best way to download any file generated by REST call is to use window.location example :
$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"));
});
Upvotes: 0
Reputation: 6803
In Angular 1.5, use the $window
service to download a file.
angular.module('app.csv').factory('csvService', csvService);
csvService.$inject = ['$window'];
function csvService($window) {
function downloadCSV(urlToCSV) {
$window.location = urlToCSV;
}
}
Upvotes: 1
Reputation: 773
I had to implement this recently. Thought of sharing what I had figured out;
To make it work in Safari, I had to set target: '_self',. Don't worry about filename in Safari. Looks like it's not supported as mentioned here; https://github.com/konklone/json/issues/56 (http://caniuse.com/#search=download)
The below code works fine for me in 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();
Upvotes: 2
Reputation: 236
The last answer worked for me for a few months, then stopped recognizing the filename, as adeneo commented ...
@Scott's answer here is working for me:
Download file from an ASP.NET Web API method using AngularJS
Upvotes: 4
Reputation: 5751
Rather than use Ajax / XMLHttpRequest / $http to invoke your WebApi method, use an html form. That way the browser saves the file using the filename and content type information in the response headers, and you don't need to work around javascript's limitations on file handling. You might also use a GET method rather than a POST as the method returns data. Here's an example form:
<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>
Upvotes: 1
Reputation: 629
The a.download is not supported by IE. At least at the HTML5 "supported" pages. :(
Upvotes: 0
Reputation: 3572
None of those worked for me in Chrome 42...
Instead my directive now uses this link
function (base64
made it work):
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();
});
}
Upvotes: 4
Reputation: 671
I used the below solution and it worked for me.
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();
}
Upvotes: 10
Reputation: 318182
Try it like :
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();
});
based on the most excellent answer in this question
Upvotes: 62