【发布时间】:2017-10-18 09:01:51
【问题描述】:
我正在我的 asp.net mvc 5 Web 应用程序中构建一个将我的数据导出为 CSV 文件的操作方法。 现在我尝试按照此链接中提到的方法https://www.codeproject.com/Articles/1078092/ASP-MVC-Export-download-Grid-contents-in-different 我在其中添加了以下类:-
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Web;
using System.Web.Mvc;
namespace MVCExport
{
/// <summary>
/// CSV file result impementation
/// </summary>
/// <typeparam name="TEntity">Entity list to transform to CSV</typeparam>
public class CsvFileResult<TEntity> : FileResult where TEntity : class
{
#region Fields
private const string DefaultContentType = "text/csv";
private string _delimiter;
private string _lineBreak;
private Encoding _contentEncoding;
private IEnumerable<string> _headers;
private IEnumerable<PropertyInfo> _sourceProperties;
private IEnumerable<TEntity> _dataSource;
private Func<TEntity, IEnumerable<string>> _map;
#endregion
#region Properties
public Func<TEntity, IEnumerable<string>> Map
{
get
{
return _map;
}
set { _map = value; }
}
public IEnumerable<TEntity> DataSource
{
get
{
return this._dataSource;
}
}
/// <summary>
/// CSV delimiter default ,
/// </summary>
public string Delimiter
{
get
{
if (string.IsNullOrEmpty(this._delimiter))
{
this._delimiter = CultureInfo.CurrentCulture.TextInfo.ListSeparator;
}
return this._delimiter;
}
set { this._delimiter = value; }
}
/// <summary>
/// Content Encoding (default is UTF8).
/// </summary>
public Encoding ContentEncoding
{
get
{
if (this._contentEncoding == null)
{
this._contentEncoding = Encoding.Unicode;
}
return this._contentEncoding;
}
set { this._contentEncoding = value; }
}
/// <summary>
/// the first line of the CSV file, column headers
/// </summary>
public IEnumerable<string> Headers
{
get
{
if (this._headers == null)
{
this._headers = typeof(TEntity).GetProperties().Select(x => x.Name);
}
return this._headers;
}
set { this._headers = value; }
}
public IEnumerable<PropertyInfo> SourceProperties
{
get
{
if (this._sourceProperties == null)
{
this._sourceProperties = typeof(TEntity).GetProperties();
}
return this._sourceProperties;
}
}
/// <summary>
/// byte order mark (BOM) .
/// </summary>
public bool HasPreamble { get; set; }
/// <summary>
/// Line delimiter \n
/// </summary>
public string LineBreak
{
get
{
if (string.IsNullOrEmpty(this._lineBreak))
{
this._lineBreak = Environment.NewLine;
}
return this._lineBreak;
}
set { this._lineBreak = value; }
}
/// <summary>
/// Get or Set the response output buffer
/// </summary>
public bool BufferOutput { get; set; }
#endregion
#region Ctor
/// <summary>
/// Creats new instance of CsvFileResult{TEntity}
/// </summary>
/// <param name="source">List of data to be transformed to csv</param>
/// <param name="fileDonwloadName">CSV file name</param>
/// <param name="contentType">Http response content type</param>
public CsvFileResult(IEnumerable<TEntity> source, string fileDonwloadName, string contentType)
: base(contentType)
{
if (source == null)
throw new ArgumentNullException("source");
this._dataSource = source;
if (string.IsNullOrEmpty(fileDonwloadName))
throw new ArgumentNullException("fileDonwloadName");
this.FileDownloadName = fileDonwloadName;
this.BufferOutput = true;
}
/// <summary>
/// Creats new instance of CsvFileResult{TEntity}
/// </summary>
/// <param name="source">List of data to be transformed to csv</param>
/// <param name="fileDonwloadName">CSV file name</param>
public CsvFileResult(IEnumerable<TEntity> source, string fileDonwloadName)
: this(source, fileDonwloadName, DefaultContentType)
{
}
/// <summary>
/// Creats new instance of CsvFileResult{TEntity}
/// </summary>
/// <param name="source">List of data to be transformed to csv</param>
/// <param name="fileDonwloadName">CSV file name</param>
/// <param name="map">Custom transformation delegate</param>
/// <param name="headers">Columns headers</param>
public CsvFileResult(IEnumerable<TEntity> source, string fileDonwloadName, Func<TEntity, IEnumerable<string>> map, IEnumerable<string> headers)
: this(source, fileDonwloadName, DefaultContentType)
{
this._headers = headers;
this._map = map;
}
#endregion
#region override
protected override void WriteFile(HttpResponseBase response)
{
response.ContentEncoding = this.ContentEncoding;
response.BufferOutput = this.BufferOutput;
var streambuffer = ContentEncoding.GetBytes(this.GetCSVData());
if (HasPreamble)
{
var preamble = this.ContentEncoding.GetPreamble();
response.OutputStream.Write(preamble, 0, preamble.Length);
}
response.OutputStream.Write(streambuffer, 0, streambuffer.Length);
}
#endregion
#region local routines
private string GetCSVHeader()
{
string csv = "";
csv = String.Join(this.Delimiter, this.Headers.Select(x => this.FormatCSV(x)));
return csv;
}
private string GetCSVData()
{
string csv = GetCSVHeader();
Func<TEntity, string> expr = x => this.Map == null ? this.FormatPropertiesCSV(x) : this.FormatMapCSV(x);
csv += this.LineBreak + String.Join(this.LineBreak, this.DataSource.Select(expr));
return csv;
}
private string FormatCSV(string str)
{
str = (str ?? "").Replace(this.Delimiter, "\"" + this.Delimiter + "\"");
str = str.Replace(this.LineBreak, "\"" + this.LineBreak + "\"");
str = str.Replace("\"", "\"\"");
return String.Format("\"{0}\"", str);
}
private string FormatPropertiesCSV(TEntity obj)
{
string csv = "";
foreach (var pi in this.SourceProperties)
{
string val = GetPropertyValue(pi, obj);
csv += FormatCSV(val) + this.Delimiter;
}
csv = csv.TrimEnd(this.Delimiter.ToCharArray());
return csv;
}
private string GetPropertyValue(PropertyInfo pi, object source)
{
try
{
var result = pi.GetValue(source, null);
return (result == null) ? "" : result.ToString();
}
catch (Exception)
{
return "Can not obtain the value";
}
}
private string FormatMapCSV(TEntity obj)
{
return String.Join(this.Delimiter, this.Map(obj).Select(x => FormatCSV(x)));
}
#endregion
}
}
然后我定义导出操作方法如下:-
public ActionResult MyExportCSV()
{
IEnumerable<Employee> dataList = _dataSource.GetAll();
return new CsvFileResult<Employee>(dataList, "toto.csv");
}
现在上述方法在几乎 99% 的情况下都运行良好,但我面临的问题是,当数据包含嵌入的逗号时,我将获得由 "" 包围的 .csv 文件中的值。这是一个例子:-
我的数据库中有以下值
test,123。现在,当我使用上述代码导出数据时,当我使用 MS excel 2010 打开 .csv 文件时,此值将显示为test","123。如图所示当我使用 Notepad++ 打开 .csv 文件时,我会得到
"test"",""123"
所以我不确定问题出在哪里?它在我的代码里面吗?还是与 MS excel 2010 相关?我的意思是最后我期望值 test,123 在 MS excel 2010 中按原样显示,而不是 test","123?
【问题讨论】:
-
您不从事制作 CSV 库的业务。您正在为您的公司编写代码。不要浪费他们的时间重新发明轮子。使用能够处理 CSV 文件细微差别的现有良好支持的库。当您搜索“CSV”时,NuGet 上最受欢迎的库是 CsvHelper。我建议你从那里开始。
-
@mason 是的,我同意你的观点,没有必要重新发明轮子......现在例如,如果我想读取 csv 文件,我使用以下库
Microsoft.VisualBasic.FileIO,而不是重新发明轮子..但我找不到任何库来导出可以轻松使用 asp.net MVC 的数据。所以我提供的链接codeproject.com/Articles/1078092/… 似乎提供了一种简单的方法,可以导出数据,我可以重用相同的代码来导出我拥有的任何模型类...... -
@mason ... 现在关于您提供的 CsvHelper 库,是否有关于我如何在 asp.net mvc 中使用它的详细文档?第二个问题,你知道是什么导致了我提到的问题吗?我的意思是如果我能修复它,那么我认为我应该没问题......或者你建议尝试使用 CsvHelper 代替?
-
当然我建议使用 CsvHelper。你重新发明了轮子。现在,您必须维护和修复某个具有 Code Project 帐户的随机人员创建的 CSV 编写库中的错误。你真的认为这是最好的利用你的时间吗?还是您认为最好利用您的时间来使用 CsvHelper,一个下载近 300 万次并被数千名开发人员使用的库,所有问题都已解决?至于让它在 ASP.NET MVC 中工作,它是一个创建 CSV 的 .NET 库。在 ASP.NET MVC 中使用它很简单。我链接到您的网站应该清楚说明
-
尝试实现 CsvHelper。如果遇到困难,请提供MCVE。我会立即删除您从代码项目中复制粘贴的这段代码。这对您的应用程序来说是一个巨大的责任,而且是在浪费您的时间。
标签: c# asp.net asp.net-mvc excel csv