javascript 从 mvc web api httpresponse 生成 csv 并通过 angularjs 接收以供下载

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20303988/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 18:11:20  来源:igfitidea点击:

generate csv from mvc web api httpresponse and receive it by angularjs for download

javascriptangularjscsvasp.net-web-apihttpresponse

提问by raberana

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:

我正在尝试从我的 web api 生成一个 CSV 文件并通过 angularjs 接收该文件。我有一个如下所示的 API 控制器:

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

在我的 angularjs 中,我有这个:

$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

比如说,在我的 API 控制器中,响应的内容是

output

{byte[152]}

[0]: 83

[1]: 101

[2]: 44

[3]: 67

[4]: 10

输出

{字节[152]}

[0]:83

[1]:101

[2]:44

[3]:67

[4]:10

When I receive this in angularjsand I put the value of contentin the console log (chrome), this is what I get:

当我在angularjs 中收到这个并将 的值放入content控制台日志(chrome)时,这就是我得到的:

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

0:"S"

1: "e"

2: ","

3: "C"

4: "?"

$promise: object

$resolved: true`

{参数:数组[1],$promise:对象,$resolved:true,$get:函数,$save:函数...}

0:"S"

1:“e”

2:“,”

3:“C”

4:“?”

$promise: 对象

$已解决:真`

  1. Why did the contentreceived in the angularjs contain characters already instead of a byte of array?

  2. How can I control the contentin such a way that I will only use the csv related data and remove $promiseand $resolved? Why are they included in the first place? How to remove them?

  3. What is the proper way of generating a csv if what I am doing is wrong? :|

  1. 为什么content在 angularjs 中接收到的已经包含字符而不是一个字节的数组?

  2. 我怎样才能控制content这种方式,我只会使用 csv 相关数据并删除$promise$resolved?为什么他们首先被包括在内?如何删除它们?

  3. 如果我在做什么是错误的,那么生成 csv 的正确方法是什么?:|

回答by raberana

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.

将有两个 API,一个 (POST) 将记住要在处理中使用的数据,另一个 (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, remember the guid returned and pass this to another api:

在 AngularJs 中,记住返回的 guid 并将其传递给另一个 api:

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

And here is the generatefileAPI controller in MVC:

这是generatefileMVC 中的API 控制器:

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.hrefwill cause the browser to automatically download the file, asking you whether to save it or not.

使用location.href会导致浏览器自动下载文件,询问您是否保存。

回答by itaylorweb

Here's how I do it: (tested in chrome)

这是我的方法:(在 chrome 中测试)

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

Incase it helps anyone else.

如果它可以帮助其他任何人。