【问题标题】:generate csv from mvc web api httpresponse and receive it by angularjs for download从 mvc web api httpresponse 生成 csv 并由 angularjs 接收以供下载
【发布时间】:2013-12-16 17:25:47
【问题描述】:

我正在尝试从我的 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;
    }

在我的 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();
        });
    };

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

输出

{字节[152]}

[0]: 83

[1]: 101

[2]: 44

[3]:67

[4]:10

当我在 angularjs 中收到此信息并将content 的值放入控制台日志(chrome)中时,我得到了:

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

0:"S"

1: "e"

2: ","

3:“C”

4:“↵”

$promise: 对象

$resolved: true`

  1. 为什么angularjs中收到的content中包含字符 已经代替了一个字节的数组?

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

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

【问题讨论】:

    标签: javascript angularjs csv asp.net-web-api httpresponse


    【解决方案1】:

    忘记更新了,但我现在找到了解决这个问题的方法:

    将有两个 API,一个 (POST) 将记住要在处理中使用的数据,另一个 (GET) 将分发文件。

    发布:

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

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

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

    这里是 MVC 中的 generatefile API 控制器:

    获取:

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

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

    【讨论】:

    • 但这会使用服务器内存...还有其他答案吗??
    【解决方案2】:

    我是这样做的:(在 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();
                }
            }
        });
    

    以防它帮助其他人。

    【讨论】:

    • 您正在使用 $resource 进行异步服务器调用并在成功函数中获取 csv/excel 数据,然后当调用锚点的点击时,它不会在此时进行服务器调用,它只是显示文件???由于弹出窗口阻止程序可能对此大惊小怪,它会在没有 target=_blank 的情况下工作吗?我以前没有使用过 $resource 并且我们的 angular 基础结构被抽象掉了,但我相信如果我愿意的话我可以做到。这个 dataUrl 有长度限制吗?长的会减慢浏览器的速度吗?你有任何代码可以显示关于reportsRepository
    • 您是正确的,使用 $resource 对服务器进行异步调用,并且锚点单击不会进行另一个调用,它只会显示文件。如果没有 target=_blank,它可以正常工作。数据 url 有长度限制,但由浏览器决定。我相信 Chrome 是 2Mb。我已经编辑了我的上一个回复以包含报告存储库
    猜你喜欢
    • 2017-11-14
    • 2016-08-16
    • 2013-12-14
    • 2016-03-02
    • 2018-07-23
    • 2015-04-18
    • 1970-01-01
    • 2019-07-20
    • 1970-01-01
    相关资源
    最近更新 更多