如何最好地生成一个CSV(逗号分隔的文本文件),以便通过ASP.NET下载?
这就是我所拥有的。有用。但是,有没有更简单或者更完善的方法呢?
一个ASPX页面,我有下载链接...
<asp:HyperLink ID="HyperLinkDownload" runat="server" NavigateUrl="~/Download.aspx">Download as CSV file</asp:HyperLink>
然后,我有了背后的Download.aspx.vb代码...
Public Partial Class Download Inherits System.Web.UI.Page Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load 'set header Response.Clear() Response.ContentType = "text/csv" Dim FileName As String = "books.csv" Response.AppendHeader("Content-Disposition", "attachment;filename=" + FileName) 'generate file content Dim db As New bookDevelopmentDataContext Dim Allbooks = From b In db.books _ Order By b.Added _ Select b Dim CsvFile As New StringBuilder CsvFile.AppendLine(CsvHeader()) For Each b As Book In Allbooks CsvFile.AppendLine(bookString(b)) Next 'write the file Response.Write(CsvFile.ToString) Response.End() End Sub Function CsvHeader() As String Dim CsvLine As New StringBuilder CsvLine.Append("Published,") CsvLine.Append("Title,") CsvLine.Append("Author,") CsvLine.Append("Price") Return CsvLine.ToString End Function Function bookString(ByVal b As Book) As String Dim CsvLine As New StringBuilder CsvLine.Append(b.Published.ToShortDateString + ",") CsvLine.Append(b.Title.Replace(",", "") + ",") CsvLine.Append(b.Author.Replace(",", "") + ",") CsvLine.Append(Format(b.Price, "c").Replace(",", "")) Return CsvLine.ToString End Function End Class
解决方案
回答
CSV格式有一些陷阱。我们是否问过自己以下问题:
- 我的数据中是否包含任何逗号?
- 我的数据中是否有任何嵌入的双引号?
- 我的任何数据都有换行符吗?
- 我需要支持Unicode字符串吗?
我在上面的代码中看到了几个问题。首先是逗号...我们正在剥离逗号:
CsvLine.Append(Format(b.Price, "c").Replace(",", ""))
为什么?在CSV中,我们应该将所有带引号的逗号括起来:
CsvLine.Append(String.Format("\"{0:c}\"", b.Price))
(或者类似的东西...我的VB不太好)。如果我们不确定是否有逗号,请在其周围加上引号。如果字符串中包含引号,则需要通过将它们加倍来对其进行转义。 "
变为""
。
b.Title.Replace("\"", "\"\"")
然后根据需要用引号将其引起来。如果字符串中包含换行符,则需要用引号将字符串引起来...是的,CSV文件中允许使用文字换行符。在人类看来,这很奇怪,但这一切都很好。
优秀的CSV编写者需要一些思考。一个好的CSV阅读器(解析器)非常困难(不,正则表达式不足以解析CSV ...它只会为我们提供95%的解析度)。
然后是Unicode ...或者更普遍的是I18N(国际化)问题。例如,我们要从格式化的价格中除去逗号。但这是假设价格的格式符合我们在美国的预期。在法国,数字格式是相反的(使用句号代替逗号,反之亦然)。底线是,尽可能使用与文化无关的格式。
虽然这里的问题是生成CSV,但不可避免地,我们将需要解析CSV。在.NET中,我发现(免费)的最佳解析器是CodeProject上的Fast CSV Reader。我实际上已经在生产代码中使用了它,它确实非常快,而且非常易于使用!
回答
我通过以下函数传递所有CSV数据:
Function PrepForCSV(ByVal value As String) As String return String.Format("""{0}""", Value.Replace("""", """""")) End Function
另外,如果我们不提供html,则可能需要使用http处理程序(.ashx文件)而不是完整的网页。如果我们在Visual Studio中创建一个新的处理程序,很可能我们可以将现有代码复制到main方法中,并且该方法将可以正常工作,并且为工作带来一点性能提升。
回答
除了Simon所说的以外,我们可能还想阅读CSV指南,并确保输出不会跨任何陷阱。
为了澄清一些事情,西蒙说:
Then surround this by quotes if you want
包含加倍双引号("")的字段将需要完全用双引号括起来。仅用双引号括起所有字段不会有任何危害,除非我们特别希望解析器除去开头和结尾的空格(而不是自己修剪空格)。
回答
如果要用冒号分隔的值转换器,则有一个名为FileHelpers的第三方开源软件。我不确定它所使用的开源许可证是什么,但是它对我有很大帮助。
回答
Page类有很多开销。由于我们只是吐出一个CSV文件,并且不需要回发,服务器控件,缓存或者其他文件,因此应将其设置为带有.ashx扩展名的处理程序。看这里。
回答
我们可以在查询本身中创建与bookString()等效的对象。我认为这是一种更简单的方法。
protected void Page_Load(object sender, EventArgs e) { using (var db = new bookDevelopmentDataContext()) { string fileName = "book.csv"; var q = from b in db.books select string.Format("{0:d},\"{1}\",\"{2}\",{3:F2}", b.Published, b.Title.Replace("\"", "\"\""), b.Author.Replace("\"", "\"\""), t.price); string outstring = string.Join(",", q.ToArray()); Response.Clear(); Response.ClearHeaders(); Response.ContentType = "text/csv"; Response.AppendHeader("Content-Disposition", string.Format("attachment;filename={0}", fileName)); Response.Write("Published,Title,Author,Price," + outstring); Response.End(); } }
回答
从数据表构建CSV文件时,我使用以下方法。 ControllerContext只是将文件写入其中的响应流对象。对我们来说,它将只是Response对象。
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; }