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? :|

Original source