通过Web导出CSV和Excel格式的表格数据
本文介绍了一个通过 Web 以 CSV 和 Excel 格式导出表格数据的示例。
引言
背景
开发 Web 应用程序时,一个常见的任务是将一些表格数据导出到 Microsoft Excel 可以打开的文件中。本文将介绍一个将数据导出为 CSV 和 Excel(xlsx)格式的示例。此示例使用了一些“MVC”功能将生成的文件暴露给 Web 浏览器,我假设读者对使用“MVC”进行 Web 应用程序开发有一定的基础。如果您是“MVC”新手,这里是一个很好的参考链接。
附加的 Visual Studio 2010 解决方案是一个简化的“MVC”Web 应用程序。
- “Models\StudentRepository.cs”文件实现了应用程序的数据模型。
- “ExcelUtilities\CSVUtility.cs”文件实现了一个实用类,用于帮助以“CSV”格式导出数据。
- “ExcelUtilities\ExcelUtility.cs”文件实现了一个实用类,用于帮助以 Excel 格式导出数据。
- 数据文件是从“Controllers\HomeController.cs”文件中实现的“MVC”控制器导出的。
- “Default.htm”文件是触发实用类生成的文件下载的 Web 界面。
要创建 Excel “xlsx”文件,本文使用了“OpenXML SDK 2.0”。您可以从这里下载此 SDK。在本文中,我将首先介绍数据模型,然后介绍两个实用类。最后,我将向您展示如何使用这两个实用类将“CSV”和 Excel 文件暴露给 Web 浏览器。
数据模型
应用程序的数据模型在“Models\StudentRepository.cs”文件中实现。
using System;
using System.Data;
namespace WebExcel.Models
{
public static class StudentRepository
{
public static DataTable GetStudents()
{
var students = new DataTable();
students.Columns.Add(new DataColumn("ID", Type.GetType("System.Int32")));
students.Columns.Add(new DataColumn("Name", Type.GetType("System.String")));
students.Columns.Add(new DataColumn
("Enrollment", Type.GetType("System.DateTime")));
students.Columns.Add(new DataColumn("Score", Type.GetType("System.Int32")));
var rand = new Random();
for (int i = 1; i <= 100; i++)
{
Object[] data = new Object[4];
data[0] = i;
data[1] = "Student Name No." + i.ToString();
data[2] = DateTime.Now;
data[3] = 60 + (int)(rand.NextDouble() * 40);
students.Rows.Add(data);
}
return students;
}
}
}
“GetStudents
”方法返回一个“DataTable”,其中包含一个随机生成的学生列表。在此示例中,我们将以“CSV
”和 Excel 格式导出学生列表。
CSV 实用工具
“CSV
”实用类实现如下:
using System;
using System.IO;
using System.Text;
using System.Web;
using System.Data;
namespace WebExcel.ExcelUtilities
{
public static class CSVUtility
{
public static MemoryStream GetCSV(DataTable data)
{
string[] fieldsToExpose = new string[data.Columns.Count];
for (int i = 0; i < data.Columns.Count; i ++ )
{
fieldsToExpose[i] = data.Columns[i].ColumnName;
}
return GetCSV(fieldsToExpose, data);
}
public static MemoryStream GetCSV(string[] fieldsToExpose, DataTable data)
{
MemoryStream stream = new MemoryStream();
using (var writer = new StreamWriter(stream))
{
for (int i = 0; i < fieldsToExpose.Length; i++)
{
if (i != 0) { writer.Write(","); }
writer.Write("\"");
writer.Write(fieldsToExpose[i].Replace("\"", "\"\""));
writer.Write("\"");
}
writer.Write("\n");
foreach (DataRow row in data.Rows)
{
for (int i = 0; i < fieldsToExpose.Length; i++)
{
if (i != 0) { writer.Write(","); }
writer.Write("\"");
writer.Write(row[fieldsToExpose[i]].ToString()
.Replace("\"", "\"\""));
writer.Write("\"");
}
writer.Write("\n");
}
}
return stream;
}
}
}
“CSVUtility
”类公开了两个“重载”方法。它们都接受“DataTable
”作为数据输入。如果我们不想将“DataTable
”中的所有字段都暴露在“CSV”文件中,可以传入“fieldsToExpose
”参数,其中包含我们想要暴露的字段的列名。每个方法都返回一个“MemoryStream”,其中包含生成“CSV”文件的数据。“CSV
”格式是一种非常强大而又简单的表示表格数据的方法。根据“Wikipedia”,创建“CSV”文件的规则如下:
- 每个记录占一行,以换行符(ASCII/LF=0x0A)或回车换行符对(ASCII/CRLF=0x0D 0x0A)结束,但是,行可以嵌入其中。
- 字段以逗号分隔。(在将逗号用作小数点分隔符的区域设置中,则改用分号作为分隔符。不同的分隔符会导致 CSV 文件交换时出现问题,例如,在法国和美国之间。)
- 在某些 CSV 实现中,会去除逗号旁边的前导和尾随空格或制表符。这种做法是有争议的,实际上 RFC 4180 明确禁止了这种做法,其中规定“空格被视为字段的一部分,不应被忽略”。
- 包含嵌入逗号的字段必须用双引号括起来。
- 包含嵌入双引号的字段必须用双引号括起来,并且每个嵌入的双引号都必须用一对双引号表示。
- 包含嵌入换行符的字段必须用双引号括起来。
- 在会去除前导或尾随空格的 CSV 实现中,包含此类空格的字段必须用双引号括起来。
- 字段始终可以用双引号括起来,无论是否必要。
- CVS 文件中的第一个记录可能包含每个字段中的列名。
您可能会注意到,本示例中的“CSVUtility
”类是“CSV
”格式的简化版本。但在大多数实际应用中,“CSVUtility
”类已经足够。
Excel 实用工具
Excel 实用类实现如下:
using System;
using System.Web;
using System.IO;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using System.Data;
namespace WebExcel.ExcelUtilities
{
public class ExcelUtility
{
// Get the excel column letter by index
public static string ColumnLetter(int intCol)
{
int intFirstLetter = ((intCol) / 676) + 64;
int intSecondLetter = ((intCol % 676) / 26) + 64;
int intThirdLetter = (intCol % 26) + 65;
char FirstLetter = (intFirstLetter > 64) ? (char)intFirstLetter : ' ';
char SecondLetter = (intSecondLetter > 64) ? (char)intSecondLetter : ' ';
char ThirdLetter = (char)intThirdLetter;
return string.Concat(FirstLetter, SecondLetter, ThirdLetter).Trim();
}
// Create a text cell
private static Cell CreateTextCell(string header, UInt32 index, string text)
{
var cell = new Cell { DataType = CellValues.InlineString,
CellReference = header + index };
var istring = new InlineString();
var t = new Text { Text = text };
istring.Append(t);
cell.Append(istring);
return cell;
}
public static MemoryStream GetExcel(DataTable data)
{
string[] fieldsToExpose = new string[data.Columns.Count];
for (int i = 0; i < data.Columns.Count; i++)
{
fieldsToExpose[i] = data.Columns[i].ColumnName;
}
return GetExcel(fieldsToExpose, data);
}
public static MemoryStream GetExcel(string[] fieldsToExpose, DataTable data)
{
MemoryStream stream = new MemoryStream();
UInt32 rowcount = 0;
// Create the Excel document
var document = SpreadsheetDocument.Create
(stream, SpreadsheetDocumentType.Workbook);
var workbookPart = document.AddWorkbookPart();
var worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
var relId = workbookPart.GetIdOfPart(worksheetPart);
var workbook = new Workbook();
var fileVersion = new FileVersion
{ ApplicationName = "Microsoft Office Excel" };
var worksheet = new Worksheet();
var sheetData = new SheetData();
worksheet.Append(sheetData);
worksheetPart.Worksheet = worksheet;
var sheets = new Sheets();
var sheet = new Sheet { Name = "Sheet1", SheetId = 1, Id = relId };
sheets.Append(sheet);
workbook.Append(fileVersion);
workbook.Append(sheets);
document.WorkbookPart.Workbook = workbook;
document.WorkbookPart.Workbook.Save();
// Add header to the sheet
var row = new Row { RowIndex = ++rowcount };
for (int i = 0; i < fieldsToExpose.Length; i++)
{
row.Append(CreateTextCell(ColumnLetter(i), rowcount, fieldsToExpose[i]));
}
sheetData.AppendChild(row);
worksheetPart.Worksheet.Save();
// Add data to the sheet
foreach (DataRow dataRow in data.Rows)
{
row = new Row { RowIndex = ++rowcount };
for (int i = 0; i < fieldsToExpose.Length; i++)
{
row.Append(CreateTextCell(ColumnLetter(i), rowcount,
dataRow[fieldsToExpose[i]].ToString()));
}
sheetData.AppendChild(row);
}
worksheetPart.Worksheet.Save();
document.Close();
return stream;
}
}
}
与“CSVUtility
”类类似,“ExcelUtility
”类也公开了两个“重载”的 public
方法。如果我们不想暴露 Excel 文件中的所有字段,可以传入“fieldsToExpose
”参数。与“CSV
”格式相比,Excel 文件要复杂得多,并支持更多功能。“ExcelUtility
”类基于“OpenXML SDK 2.0”。使用此 SDK,您可以创建复杂的 Excel 文件。在此示例中,“ExcelUtility
”仅创建纯“xlsx”文件。
MVC 控制器
本示例中的“MVC
”控制器实现如下:
using System;
using System.Web;
using System.Web.Mvc;
using WebExcel.Models;
using WebExcel.ExcelUtilities;
using System.IO;
namespace WebExcel.Controllers
{
[HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
var students = StudentRepository.GetStudents();
return new RedirectResult("~/Default.htm");
}
public void GetCSV()
{
var students = StudentRepository.GetStudents();
MemoryStream stream = CSVUtility.GetCSV(students);
var filename = "ExampleCSV.csv";
var contenttype = "text/csv";
Response.Clear();
Response.ContentType = contenttype;
Response.AddHeader("content-disposition", "attachment;filename=" + filename);
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.BinaryWrite(stream.ToArray());
Response.End();
}
public void GetExcel()
{
var students = StudentRepository.GetStudents();
MemoryStream stream = ExcelUtility.GetExcel(students);
var filename = "ExampleExcel.xlsx";
var contenttype =
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
Response.Clear();
Response.ContentType = contenttype;
Response.AddHeader("content-disposition", "attachment;filename=" + filename);
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Response.BinaryWrite(stream.ToArray());
Response.End();
}
}
}
使用这两个实用类,我们可以轻松地通过“MVC
”操作方法将“CSV”和 Excel 文件导出到 Web 浏览器。“GetCSV
”方法导出“CSV”文件,而“GetExcel
”导出“xlsx”文件。
Default.htm 文件
“Default.htm”文件实现如下:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Web Excel Test</title>
<link href="Content/Site.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div>
<a href="Home/GetCSV" class="siteButton">Get CSV</a>
<a href="Home/GetExcel" class="siteButton">Get Excel</a>
</div>
</body>
</html>
这是一个非常简单的 HTML 文件。每个超链接都指向一个“MVC
”操作方法,以下载“CSV”或“xlsx”文件。CSS 类“siteButton
”使这两个超链接在 Web 浏览器中看起来像按钮。
运行应用程序
现在我们完成了这个示例应用程序的开发,可以开始测试了。当应用程序启动时,Web 浏览器中会显示两个按钮。
然后我们可以单击按钮来获取文件。下图显示了从这个示例应用程序获得的 Excel 文件。
关注点
- 本文介绍了一个通过 Web 以“CSV”以及 Excel 格式导出表格数据的示例。
- 尽管本示例展示了如何通过 Web 导出文件,但您也可以轻松地使用这两个实用类在桌面应用程序中创建文件。
- 这两个实用类从“
DataTable
”对象获取数据。这可能并非总是最方便的方法。如果您发现有其他更方便的方式将数据传递给实用类,请随时对类进行修改。 - 本文展示了导出“CSV”和 Excel 文件的两种方法。在大多数情况下,我认为导出“CSV”文件对用户来说更方便,因为它不仅被 Microsoft Excel 支持,还被许多其他数据加载软件应用程序和数据库支持。
- 如果您自己运行该应用程序,您可能需要在计算机上安装正确版本的 Microsoft Office 才能打开文件。在我自己的测试中,我注意到我计算机上的“Open Office”版本未能打开“xlsx”文件。在安装了 Microsoft Office 2007 的计算机上,我打开文件没有任何问题。
- 我希望您喜欢我的文章,希望本文能以某种方式帮助您。
历史
- 首次修订 - 2011/10/3