Create a CSV file through an MVC Web API HttpResponse and retrieve it using AngularJS for downloading

I am attempting to create a CSV file from my web API and retrieve that file using angularjs. Below is an example of my API controller:

 [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 code, I have the following:

$scope.save = function () {
        var csvInput= extractDetails();

        // File is an angular resource. We call its save method here which
        // accesses the API above to retrieve the CSV content
        File.save(csvInput, function (content) {
            console.log(content);

            // This currently 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();
        });
    };

For instance, if the response content in my API controller is:

output

{byte[152]}

[0]: 83

[1]: 101

[2]: 44

[3]: 67

[4]: 10

When I receive this in angularjs and log the value of content, this is what is displayed:

{Parameters: Array[1], $promise: Object, $resolved: true, $get: function, $save: function…}

0:"S"

1: "e"

2: ","

3: "C"

4: "↵"

$promise: object

$resolved: true`

  1. Why does the content received in angularjs already contain characters instead of a byte array?

  2. How can I manipulate the content to focus only on the CSV data and exclude $promise and $resolved? Why are they included initially? How can they be removed?

  3. If my current approach is incorrect, what is the proper way to generate a CSV file? :(

Answer №1

Recently discovered a solution for this issue:

Two separate APIs will be utilized for handling the data processing - one for storing the data (POST) and the other for retrieving the file (GET).

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, store the returned GUID and pass it to another API endpoint:

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

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

Utilizing location.href triggers automatic downloading of the file by the browser.

Answer №2

Here is my method for achieving this (tested in Chrome):

    // This is a WebAPI controller action
    public IHttpActionResult Get(string rpt, DateTime date)
    {
        List<DailyMIReportViewModel> list = new List<DailyMIReportViewModel>();

        // Perform operations to generate a list of items

        // Download Requested
        if (rpt == "dailymidl")
        {
            // Create a byte array of the CSV data
            byte[] csvData = WriteCsvWithHeaderToMemory(list);
            // Create FileContentResult with the CSV byte array
            FileContentResult result = new FileContentResult(csvData, "application/octet-stream");
            // Set the filename in the FileContentResult
            result.FileDownloadName = "Report.csv";

            return Ok(result);
        }
        // Data Requested
        return Ok(list);

        // Client-side AngularJS code
        // Function called on button click
        $scope.generateMIDownload = function (forDate) {
            // Using $resource to return a promise with FileContentResult payload
            reportsRepository.dailymidl(forDate).$promise.then(
            function (data) {
                // Success
                // NOTE: adding base64 made it work

                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) {
                // Error handling
            });
        }

        // Reports Repository reference
        angular.module('msgnr').factory('reportsRepository', function ($resource) {
            return {
                dailymidl: function (date) {
                    return $resource('/api/Report/', { rpt: 'dailymidl', date: date, toDate: date }).get();
            }
        }
    });

This information may be useful for others as well.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What are the steps for sorting objects in an array by their data type attribute?

I am currently working with a JavaScript array of people objects that is dynamically rendered from JS. My goal is to implement a filter on the objects based on the selection made in the dropdown menu and matching it with the department attribute specified ...

Perform a task if a checkbox is marked or unmarked

Currently, I am working on an HTML form that includes a checkbox asking users if they have a phone number. I am facing an issue where I want to implement two actions: 1. If the user checks the checkbox, a new input element should appear on the same page ...

Locating the dot character within regular expression challenges

Having difficulty replacing terms like "joe." using a regular expression. Look at the snippet below: var phrases = new Array("joe","sam"); sentence = "joe.id was here as well as sam.id"; for(i = 0; i < phrases.length; i++) { regex = new RegEx ...

effective methods for setting up live reload feature for an Angular application

I've been attempting to implement grunt livereload for a basic Angular application. Below is the section from my gruntfile: watch: { livereload: { files: [ '*.{html,js}', & ...

Modify capital letters to dashed format in the ToJSON method in Nest JS

I am working with a method that looks like this: @Entity() export class Picklist extends BaseD2CEntity { @ApiHideProperty() @PrimaryGeneratedColumn() id: number; @Column({ name: 'picklist_name' }) @IsString() @ApiProperty({ type: Str ...

Express not invoking Passport LocalStrategy

I added a console.log statement in the LocalStrategy callback of passport.js, but it never seemed to execute. I am using Sequelize for ORM with MySQL. passport.js const LocalStrategy = require('passport-local').Strategy const passport = require( ...

Tips for sorting through JSON Data to isolate a particular day

I developed a Food-App that displays a different menu every day. I retrieve the local JSON Data using Axios and attempt to filter the mapped menu with .filter. My issue lies in not being able to filter specific days. I attempted to restructure the JSON Da ...

Remove an item from a complex JSON structure based on the specified name. The function will then return the

Hey there, I'm just starting out with react native and I have an array of objects. My goal is to remove an inner object from this JSON data. [ { Key: 1, exchnageArr: [ { name: ”FX” }, { name: ”MK” ...

How to customize the text color of the notchedOutline span in Material UI

I'm currently working on customizing the textfield in materialUI. This is what I am trying to modify. I am facing challenges in changing the color of the span tag that contains the text, and also in maintaining the border color when the textfield is ...

convert JSON to Java object using GSON with a map

Here is the structure of my Java POJO: public class MyPersonTO{ String name; String surname; Map<String, Double> categories; } Currently, I am using the Gson library, but I'm uncertain about the formatting of my JSON string and the obje ...

Ways to Access a Variable from One Controller to Another Controller without Utilizing Scope

Just starting out with angularjs, I have an issue that needs quick solutions. How do I pass a variable from one controller to another without using scope or Service? ...

Issues with loading shared examples in Jasmine and AngularJS

A common complaint from Jasmine is that there are no tests in one of my shared specs, despite the presence of specifications: Spec 'mysite ProductsIndexCtrl behaves like an index controller' has no expectations. My spec file appears as follows: ...

Ensure that the ng-view element has a height of 100% and

Currently, I am working on developing an AngularJS app and my index page consists of a ng-view where all the content is injected using ui-router: <header> <nav class="navbar"> //content for navbar will go here </nav </hea ...

Launch a new window for a div when a button is clicked

Hey everyone, I need some help with opening a div in a new window. Currently, I am using window.open and it is working fine. Here is the code snippet: <div id="pass">pass this to the new window</div> <a href="#" onclick="openWin()">clic ...

Is there a way to allow only the block code to shift while keeping the other span tags stationary?

Is it possible to change the text without affecting other span tags in the code? I want to make sure only this specific text is updated. How can I achieve that? var para_values = [ { content: "BRAND " }, { content: "MISSION" } ]; functi ...

What is the functionality of require(../) in node.js?

When encountering var foo=require(../), what specific modules does node.js search for? It may appear to look in the directory above the current one, but what exactly is it seeking and how does it function? Is there a comparison with include in C or impor ...

Combine strings in an array of objects

I have an array containing objects with a "Closed" property that holds numerical values. I want to loop through the array and concatenate all the "Closed" property values found in each object. For example, in the given array, the final result should be 12 ...

Issue with passing parameter values in MVC Html.ActionLink

Currently, I am experimenting with MVC for a demonstration and have managed to put together some components. However, I am encountering difficulties with an Html.ActionLink. The goal is to display a series of dropdown lists to the user that must be selecte ...

Is there a method to incorporate a scroll event into the ng-multi-selectdropdown, a npm package?

Query: I need to incorporate a scroll event in the given html code that triggers when the div is scrolled. However, I am facing issues with implementing a scroll event that works when the elements are being scrolled. <ng-mult ...

Choose a JavaScript function by clicking on the HTML text

As a beginner in the world of coding, I have been diving into JavaScript, HTML, and CSS. Inspired by fictional supercomputers like the Batcomputer and Jarvis, I've challenged myself to create my own personal assistant to manage tasks, games, programs, ...