generate csv from mvc web api httpresponse and receive it by angularjs for download
angularjs, asp.net-web-api, csv, httpresponse, javascript
Solution
Forgot to update this, but i now found a way to solve this:
There will be two API's, one (POST) will remember the data to be used in the processing and another one (GET) which will dispense the file.
POST:
[HttpPost]
public async Task<HttpResponseMessage> BuildFile(FileParameters fileParams)
{
var guid = Guid.NewGuid().ToString();
if (fileParams!= null)
{
await Task.Run(() => FileContents.Add(guid, fileParams));
return this.Request.CreateResponse(HttpStatusCode.OK, new { Value = guid });
}
return this.Request.CreateErrorResponse(HttpStatusCode.BadRequest, "Invalid data");
}
In AngularJs, remember the guid returned and pass this to another api:
location.href = '/api/file/generatefile' + '?guid=' + generatedGuidFromAPI + '&reportName=' + $scope.reportName;
And here is the `generatefile` API controller in MVC:
GET:
[HttpGet]
public async Task<HttpResponseMessage> GenerateFile(string guid, string reportName)
{
byte[] output = null;
if (FileContents.ContainsKey(guid))
{
await Task.Run(() =>
{
using (var stream = new MemoryStream())
{
this.CreateFile(FileContents[guid], stream);
stream.Flush();
output = stream.ToArray();
}
});
}
FileContents.Remove(guid);
if (output != null)
{
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 = reportName + ".csv"
};
return result;
}
return this.Request.CreateErrorResponse(HttpStatusCode.NoContent, "No record found");
}
using `location.href` will cause the browser to automatically download the file, asking you whether to save it or not.
Problem
I am trying to generate a CSV file from my web api and receive that file through angularjs. I have an API controller like below: ``` [HttpPost] public HttpResponseMessage GenerateCSV(FieldParameters fieldParams) { var output = new byte[] { }; if (fieldParams!= null) { using (var stream = new MemoryStream()) { this.Serialize(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 = "Fields.csv" }; return result; } ``` In my angularjs, i have this: ``` $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) { console.log(content); // only creates a csv file with "[object Object]" written in it var hiddenElement = document.createElement('a'); hiddenElement.href = 'data:text/csv;charset=utf-8,\uFEFF' + encodeURI(content.Parameters); hiddenElement.target = '_blank'; hiddenElement.download = 'myFile.csv'; hiddenElement.click(); }); }; ``` Lets say for example, in my API controller, the content of response is output {byte[152]} [0]: 83 [1]: 101 [2]: 44 [3]: 67 [4]: 10 When I receive this in angularjs and I put the value of `content` in the console log (chrome), this is what I get: {Parameters: Array[1], $promise: Object, $resolved: true, $get: function, $save: function…} 0:"S" 1: "e" 2: "," 3: "C" 4: "↵" $promise: object $resolved: true` Why did the `content` received in the angularjs contain characters already instead of a byte of array? How can I control the `content` in such a way that I will only use the csv related data and remove `$promise` and `$resolved`? Why are they included in the first place? How to remove them? What is the proper way of generating a csv if what I am doing is wrong? :|