将数据写入CSV文件并进行导出?

27
在C# ASP.net中,有人能向我展示如何将数组/列表中的条目写入服务器上的CSV文件,然后打开该文件吗?我认为第二部分可能是 - Response.Redirect("http://myserver.com/file.csv"),但不确定如何在服务器上编写该文件。
此外,如果许多用户访问此页面,每次生成新的CSV文件是否更好,还是覆盖同一文件?如果两个用户尝试访问相同的CSV文件,是否会出现读/写/锁定问题等?
更新:
这可能是一个愚蠢的问题,我已经在Google上搜索过,但找不到明确的答案 - 如何在C# ASP.net中将CSV文件写入Web服务器并导出它?我知道如何生成它,但我想将它保存到www.mysite.com/my.csv,然后导出它。

CSV文件对于所有用户都是相同的吗? - TheGeekYouNeed
6个回答

60
Rom,你做错了。你不想将文件写入磁盘以便IIS可以提供它们。这会增加安全隐患并增加复杂性。你真正需要做的就是直接将CSV保存到响应流中。
以下是场景:用户希望下载CSV。用户提交包含所需CSV详细信息的表单。您准备好CSV,然后向用户提供指向可用于构建CSV文件并将其写入响应流的aspx页面的URL。用户单击链接。aspx页面为空;在页面代码后台中,您只需将CSV写入响应流并结束即可。
您可以将以下内容添加到(我认为这是正确的)Load事件中:
string attachment = "attachment; filename=MyCsvLol.csv";
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.ClearHeaders();
HttpContext.Current.Response.ClearContent();
HttpContext.Current.Response.AddHeader("content-disposition", attachment);
HttpContext.Current.Response.ContentType = "text/csv";
HttpContext.Current.Response.AddHeader("Pragma", "public");

var sb = new StringBuilder();
foreach(var line in DataToExportToCSV)
  sb.AppendLine(TransformDataLineIntoCsv(line));

HttpContext.Current.Response.Write(sb.ToString());

将代码写入响应流 从这里复制


1
使用Response.End()时要小心。更多信息:https://dev59.com/B3NA5IYBdhLWcg3wHp-B - Jon Schneider

27

这里有一个非常简单的免费开源CsvExport类,适用于C#。底部提供了一个ASP.NET MVC示例。

https://github.com/jitbit/CsvExport

它能够自动处理换行符、逗号、转义引号以及与MS Excel兼容性等问题...只需将一个短的.cs文件添加到您的项目中即可使用。

(声明:我是其中的贡献者之一)


8
我个人希望感谢你的分享和出色的工作。 - LikePod

8

这里是一个CSV操作结果,它将DataTable转换为CSV格式。您可以从视图中返回此结果,它会提示用户下载文件。您应该可以轻松地将其转换为兼容List的形式,甚至只需将列表放入DataTable中。

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Data;

namespace Detectent.Analyze.ActionResults
{
    public class CSVResult : ActionResult
    {
        /// <summary>
        /// Converts the columns and rows from a data table into an Microsoft Excel compatible CSV file.
        /// </summary>
        /// <param name="dataTable"></param>
        /// <param name="fileName">The full file name including the extension.</param>
        public CSVResult(DataTable dataTable, string fileName)
        {
            Table = dataTable;
            FileName = fileName;
        }

        public string FileName { get; protected set; }
        public DataTable Table { get; protected set; }




        public override void ExecuteResult(ControllerContext context)
        {
            StringBuilder csv = new StringBuilder(10 * Table.Rows.Count * Table.Columns.Count);

            for (int c = 0; c < Table.Columns.Count; c++)
            {
                if (c > 0)
                    csv.Append(",");
                DataColumn dc = Table.Columns[c];
                string columnTitleCleaned = CleanCSVString(dc.ColumnName);
                csv.Append(columnTitleCleaned);
            }
            csv.Append(Environment.NewLine);
            foreach (DataRow dr in Table.Rows)
            {
                StringBuilder csvRow = new StringBuilder();
                for(int c = 0; c < Table.Columns.Count; c++)
                {
                    if(c != 0)
                        csvRow.Append(",");

                    object columnValue = dr[c];
                    if (columnValue == null)
                        csvRow.Append("");
                    else
                    {
                        string columnStringValue = columnValue.ToString();


                        string cleanedColumnValue = CleanCSVString(columnStringValue);

                        if (columnValue.GetType() == typeof(string) && !columnStringValue.Contains(","))
                        {
                            cleanedColumnValue = "=" + cleanedColumnValue; // Prevents a number stored in a string from being shown as 8888E+24 in Excel. Example use is the AccountNum field in CI that looks like a number but is really a string.
                        }
                        csvRow.Append(cleanedColumnValue);
                    }
                }
                csv.AppendLine(csvRow.ToString());
            }

            HttpResponseBase response = context.HttpContext.Response;
            response.ContentType = "text/csv";
            response.AppendHeader("Content-Disposition", "attachment;filename=" + this.FileName);
            response.Write(csv.ToString());
        }

        protected string CleanCSVString(string input)
        {
            string output = "\"" + input.Replace("\"", "\"\"").Replace("\r\n", " ").Replace("\r", " ").Replace("\n", "") + "\"";
            return output;
        }
    }
}

非常感谢。您帮我理清了 CSV 文件的逻辑,以及如何从浏览器下载。 - Soader03

6
关于Will的回答,你可以将HttpContext.Current.Response.End();替换为 HttpContext.Current.ApplicationInstance.CompleteRequest();。原因是Response.End()会抛出一个System.Threading.ThreadAbortException,它会终止线程。如果你有异常记录器,那么它将充斥着ThreadAbortExceptions,而在这种情况下,它是预期的行为。
直观地说,发送CSV文件到浏览器不应该引发异常。
更多信息请参见Response.End()是否有害?

1

如何写入文件(在谷歌上轻松搜索)... 第一个搜索结果

至于每次用户访问页面时创建文件...每个访问将代表自己行事。您的业务案例将决定其行为。

情况1-相同的文件但不更改(此类型的情况可以有多种定义方式)

  • 您将拥有创建文件的逻辑,仅在需要时创建文件,并且仅在不需要生成文件时访问文件。

情况2-每个用户都需要生成自己的文件

  • 您将决定如何识别每个用户,为每个用户创建一个文件并访问他们应该看到的文件...这可以很容易地与情况1合并。然后,在提供内容后删除文件或不删除文件(如果需要持久性)。

情况3-相同的文件但每次访问都需要生成

  • 使用情况2,这将导致每次生成,但一旦访问就会清除。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接