generar csv desde mvc web api httpresponse y recibirlo por angularjs para download

Intento generar un file CSV desde mi API web y recibir ese file a través de angularjs. Tengo un controller API como a continuación:

[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; } 

En mi angularjs, tengo esto:

 $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(); }); }; 

Digamos, por ejemplo, en mi controller API, el contenido de la respuesta es

salida

{byte [152]}

[0]: 83

[1]: 101

[2]: 44

[3]: 67

[4]: 10

Cuando recibo esto en angularjs y coloco el valor del content en el logging de la console (chrome), esto es lo que obtengo:

{Parámetros: Matriz [1], $ promise: Objeto, $ resuelto: verdadero, $ get: function, $ save: function …}

0: "S"

1: "e"

2: ","

3: "C"

4: "↵"

$ promise: object

$ resolved: true`

  1. ¿Por qué el content recibido en el angularjs ya contiene caracteres en lugar de un byte de matriz?

  2. ¿Cómo puedo controlar el content de tal manera que solo usaré los datos relacionados con csv y eliminaré $promise y $resolved ? ¿Por qué están incluidos en primer lugar? ¿Cómo eliminarlos?

  3. ¿Cuál es la forma correcta de generar un csv si lo que estoy haciendo está mal? : |

Olvidé actualizar esto, pero ahora encontré una manera de resolver esto:

Habrá dos API, una (POST) recordará los datos que se utilizarán en el procesamiento y otra (GET) que distribuirá el file.

ENVIAR:

  [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"); } 

En AngularJs, restring el guid devuelto y pasa esto a otra api:

  location.href = '/api/file/generatefile' + '?guid=' + generatedGuidFromAPI + '&reportName=' + $scope.reportName; 

Y aquí está el controller de API generatefile en MVC:

OBTENER:

  [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"); } 

el uso de location.href hará que el browser descargue automáticamente el file y le preguntará si desea savelo o no.

Así es como lo hago: (probado en cromo)

  // WebAPI controller action public IHttpActionResult Get(string rpt, DateTime date) { List<DailyMIReportViewModel> list = new List<DailyMIReportViewModel>(); // Do some stuff to generate list of items // Download Requested if (rpt == "dailymidl") { // Create byte array of csv byte[] csvData = WriteCsvWithHeaderToMemory(list); // create FileContentResult of cdv byte array FileContentResult result = new FileContentResult(csvData, "application/octet-stream"); // set filename in FileContentResult result.FileDownloadName = "Report.csv"; return Ok(result); } // Data Requested return Ok(list); // Client-side angularjs // Called on button click $scope.generateMIDownload = function (forDate) { // Using $resource to return promise with FileContentResult payload reportsRepository.dailymidl(forDate).$promise.then( function (data) { //ok // NOTE: the base64 part is what got it working var dataUrl = 'data:application/octet-stream;base64,' + data.FileContents var link = document.createElement('a'); angular.element(link) .attr('href', dataUrl) .attr('download', data.FileDownloadName) .attr('target','_blank') link.click(); }, function (response) { //not ok }); } // Reports Repository (for ref) angular.module('msgnr').factory('reportsRepository', function ($resource) { return { dailymidl: function (date) { return $resource('/api/Report/', { rpt: 'dailymidl', date: date, toDate: date }).get(); } } }); 

En caso de que ayude a alguien más.