65.9K
CodeProject 正在变化。 阅读更多。
Home

通过Web导出CSV和Excel格式的表格数据

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.75/5 (6投票s)

2011年10月3日

CPOL

6分钟阅读

viewsIcon

94456

downloadIcon

3325

本文介绍了一个通过 Web 以 CSV 和 Excel 格式导出表格数据的示例。

引言

本文介绍了一个通过 Web 以“CSV”以及“Excel”格式导出表格数据的示例。

背景

开发 Web 应用程序时,一个常见的任务是将一些表格数据导出到 Microsoft Excel 可以打开的文件中。本文将介绍一个将数据导出为 CSV 和 Excel(xlsx)格式的示例。此示例使用了一些“MVC”功能将生成的文件暴露给 Web 浏览器,我假设读者对使用“MVC”进行 Web 应用程序开发有一定的基础。如果您是“MVC”新手,这里是一个很好的参考链接。

SolutionExplorer.jpg

附加的 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 浏览器中会显示两个按钮。

RunApplication.jpg

然后我们可以单击按钮来获取文件。下图显示了从这个示例应用程序获得的 Excel 文件。

Excel.jpg

关注点

  • 本文介绍了一个通过 Web 以“CSV”以及 Excel 格式导出表格数据的示例。
  • 尽管本示例展示了如何通过 Web 导出文件,但您也可以轻松地使用这两个实用类在桌面应用程序中创建文件。
  • 这两个实用类从“DataTable”对象获取数据。这可能并非总是最方便的方法。如果您发现有其他更方便的方式将数据传递给实用类,请随时对类进行修改。
  • 本文展示了导出“CSV”和 Excel 文件的两种方法。在大多数情况下,我认为导出“CSV”文件对用户来说更方便,因为它不仅被 Microsoft Excel 支持,还被许多其他数据加载软件应用程序和数据库支持。
  • 如果您自己运行该应用程序,您可能需要在计算机上安装正确版本的 Microsoft Office 才能打开文件。在我自己的测试中,我注意到我计算机上的“Open Office”版本未能打开“xlsx”文件。在安装了 Microsoft Office 2007 的计算机上,我打开文件没有任何问题。
  • 我希望您喜欢我的文章,希望本文能以某种方式帮助您。

历史

  • 首次修订 - 2011/10/3
© . All rights reserved.