好的,我希望你能在C#中创建一个.csv文件。我一直在寻找资料,发现很多人都在使用system.IO.memorystream和system.io.streamwriter。
问题是:我有一个Web应用程序。我想让用户能够导出到Excel。问题是,Excel不能安装在服务器上(不要问为什么)。我想能够为报告编写一个.csv表格导出。现在,所有报告的标题和数据都将不同(通过循环解决此问题)。有没有人有示例或更好的资源供我查阅?
这是我通常采取的方法。虽然可能不是最有效的方法。
/// <summary>
/// Generates the contents of the log file.
/// </summary>
/// <returns>The contents of the log file.</returns>
internal string GenerateLogFile()
{
StringBuilder csvExport = new StringBuilder();
csvExport.AppendLine(Resources.CSVHeader);
foreach (DataRow row in this.logEntries.Rows)
{
csvExport.AppendLine(
string.Format(
"\"{0}\",\"{1}\",\"{2}\",\"{3}\",\"{4}\",\"{5}\",\"{6}\",\"{7}\",\"{8}\", \"{9}\"",
row[ColumnNames.LogTime], row[ColumnNames.Field1], row[ColumnNames.Field2], row[ColumnNames.Field3], row[ColumnNames.Field4], row[ColumnNames.Field5], row[ColumnNames.Field6], row[ColumnNames.Field7], row[ColumnNames.Field8], row[ColumnNames.Field9]));
}
return csvExport.ToString();
}
/// <summary>
/// Adds the CSV file to the response.
/// </summary>
/// <param name="csvExportContents">The contents of the CSV file.</param>
internal void DisplayLogFile(string csvExportContents)
{
byte[] data = new ASCIIEncoding().GetBytes(csvExportContents);
HttpContext.Current.Response.Clear();
HttpContext.Current.Response.ContentType = "APPLICATION/OCTET-STREAM";
HttpContext.Current.Response.AppendHeader("Content-Disposition", "attachment; filename=Export.csv");
HttpContext.Current.Response.OutputStream.Write(data, 0, data.Length);
HttpContext.Current.Response.End();
}
"
的值会如何被转义。 - Jon Hannaprivate void WriteItem<T>(StreamWriter sr, T item)
{
string itemString = item.ToString();
if(itemString.IndexOfAny(new char[] { '"', ',', '\n', '\r' }) != -1)//skip test and always escape for different speed/filesize optimisation
{
sr.Write('"');
sr.Write(itemString.Replace("\"", "\"\""));
sr.Write('"');
}
else
sr.Write(itemString);
}
private void WriteLine<T>(StreamWriter sr, IEnumerable<T> line)
{
bool first = true;
foreach(T item in line)
{
if(!first)
sr.Write(',');
first = false;
WriteItem(sr, item);
}
}
private void WriteCSV<T>(StreamWriter sr, IEnumerable<IEnumerable<T>> allLines)
{
bool first = true;
foreach(IEnumerable<T> line in allLines)
{
if(!first)
sr.Write('\n');
first = false;
WriteLine(sr, line);
}
}
private void WriteCSV<T>(HttpResponse response, IEnumerable<IEnumerable<T>> allLines)
{
response.ContentType = "text/csv";
WriteCSV(response.Output, allLines);
}
在发送文件时,建议同时发送content-disposition头部,并指定推荐的文件名。
编辑:最近,在需要在枚举项之间插入操作(例如上面的逗号和换行符)的情况下,我更喜欢直接处理枚举器,然后将第一个元素与其余元素分开处理,而不是保持一个不断被检查的布尔值。我开始将此视为一种微小的优化,但现在发现这更好地表达了不同于第一个元素的代码路径。因此,我现在会将上述代码编写为:
private void WriteLine<T>(StreamWriter sr, IEnumerable<T> line)
{
using(var en = line.GetEnumerator())
if(en.MoveNext())
{
WriteItem(sr, en.Current);
while(en.MoveNext())
{
sr.Write(',');
WriteItem(sr, en.Current);
}
}
private void WriteCSV<T>(StreamWriter sr, IEnumerable<IEnumerable<T>> allLines)
{
using(var en = allLines.GetEnumerator())
if(en.MoveNext())
{
WriteLine(sr, en.Current);
while(en.MoveNext())
{
sr.Write('\n');
WriteLine(sr, en.Current);
}
}
}
System.IO.MemoryStream
和System.IO.StreamWriter
的示例。仅在使用Office Interop时需要Excel。使用StringWriter,您只需创建一个文件并添加一些响应信息即可。只有在打开文件时才需要Excel。
private async Task<string> WriteCSV<ViewModel>(IEnumerable<ViewModel> viewModels, string path)
{
Type itemType = typeof(ViewModel);
var props = itemType.GetProperties(BindingFlags.Public | BindingFlags.Instance)
.OrderBy(p => p.Name);
var blobName = string.Empty;
using (var ms = new MemoryStream())
{
using (var writer = new System.IO.StreamWriter(ms))
{
writer.WriteLine(string.Join(", ", props.Select(p => p.Name)));
foreach (var item in viewModels)
{
writer.WriteLine(string.Join(", ", props.Select(p => p.GetValue(item, null))));
}
}
}
}
"
、,
或换行字符,则也需要对其进行转义。相关代码请查看我的答案。 - Jon Hanna