在ASP.NET中生成一个Excel文件
我正要为ASP.NET应用程序(VB.NET代码隐藏)添加一个部分,以允许用户将数据作为Excel文件返回给我们,我将根据数据库数据生成这些数据。 虽然有几种方法可以做到这一点,但每种方法都有其缺陷。 你将如何返回数据? 我正在寻找尽可能干净,直接的东西。
CSV
优点:
- 简单
缺点:
- 它可能无法在其他语言环境或不同的Excelconfiguration(即列表分隔符)
- 无法应用格式,公式等
HTML
优点:
- 还很简单
- 支持简单的格式和公式
缺点:
- 您必须将该文件命名为xls,Excel可能会警告您打开非本机Excel文件
- 每个工作簿一个工作表
OpenXML(Office 2007 .XLSX)
优点:
- 本机Excel格式
- 支持所有的Excelfunction
- 不要求安装Excel的副本
- 可以生成数据透视表
- 可以使用开源项目EPPlus生成
缺点:
- Excel 2007以外的兼容性有限(现在不应该是个问题)
- 复杂,除非你使用第三方组件
SpreadSheetML(开放式XML)
优点:
- 与原生Excel格式相比较简单
- 支持大多数Excelfunction:格式化,样式,公式,每个工作簿多个工作表
- Excel不需要安装使用它
- 没有第三方库需要 – 只需写出你的XML
- 文档可以通过Excel XP / 2003/2007打开
缺点:
- 缺乏良好的文件
- 旧版本的Excel不支持(2000之前)
- 只写,因为一旦你打开它并从Excel进行更改,它将被转换为本机Excel。
XLS(由第三方组件生成)
优点:
- 用所有格式,公式等生成本地Excel文件
缺点:
- 成本钱
- 添加依赖关系
COM Interop
优点:
- 使用本机Microsoft库
- 阅读对原生文档的支持
缺点:
- 非常慢
- 依赖/版本匹配问题
- 并发/数据完整性问题,供网站在阅读时使用
- 非常慢
- 缩放Web使用问题(与并发不同):需要在服务器上创build大量Excel应用程序实例
- 需要Windows
- 我提到它很慢吗?
您可以将数据输出为html表格单元格,在其上添加一个.xls
或.xlsx
扩展名,Excel将打开它,就像它是一个本机文档一样。 你甚至可以这样做一些有限的格式和公式计算,所以它比CSV更强大。 另外,输出一个html表格应该很容易从ASP.Net这样的web平台上完成;)
如果您需要Excel工作簿中的多个工作表或命名工作表,则可以通过名为SpreadSheetML
的XML模式执行类似的SpreadSheetML
。 这不是 Office 2007附带的新格式,而是与Excel 2000完全不同的东西。解释其工作原理的最简单方法是以下示例:
<?xml version="1.0"?> <?mso-application progid="Excel.Sheet"?> <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet" xmlns:html="http://www.w3.org/TR/REC-html40"> <DocumentProperties xmlns="urn:schemas-microsoft-com:office:office"> <Author>Your_name_here</Author> <LastAuthor>Your_name_here</LastAuthor> <Created>20080625</Created> <Company>ABC Inc</Company> <Version>10.2625</Version> </DocumentProperties> <ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel"> <WindowHeight>6135</WindowHeight> <WindowWidth>8445</WindowWidth> <WindowTopX>240</WindowTopX> <WindowTopY>120</WindowTopY> <ProtectStructure>False</ProtectStructure> <ProtectWindows>False</ProtectWindows> </ExcelWorkbook> <Styles> <Style ss:ID="Default" ss:Name="Normal"> <Alignment ss:Vertical="Bottom" /> <Borders /> <Font /> <Interior /> <NumberFormat /> <Protection /> </Style> </Styles> <Worksheet ss:Name="Sample Sheet 1"> <Table ss:ExpandedColumnCount="2" x:FullColumns="1" x:FullRows="1" ID="Table1"> <Column ss:Width="150" /> <Column ss:Width="200" /> <Row> <Cell><Data ss:Type="Number">1</Data></Cell> <Cell><Data ss:Type="Number">2</Data></Cell> </Row> <Row> <Cell><Data ss:Type="Number">3</Data></Cell> <Cell><Data ss:Type="Number">4</Data></Cell> </Row> <Row> <Cell><Data ss:Type="Number">5</Data></Cell> <Cell><Data ss:Type="Number">6</Data></Cell> </Row> <Row> <Cell><Data ss:Type="Number">7</Data></Cell> <Cell><Data ss:Type="Number">8</Data></Cell> </Row> </Table> </Worksheet> <Worksheet ss:Name="Sample Sheet 2"> <Table ss:ExpandedColumnCount="2" x:FullColumns="1" x:FullRows="1" ID="Table2"> <Column ss:Width="150" /> <Column ss:Width="200" /> <Row> <Cell><Data ss:Type="String">A</Data></Cell> <Cell><Data ss:Type="String">B</Data></Cell> </Row> <Row> <Cell><Data ss:Type="String">C</Data></Cell> <Cell><Data ss:Type="String">D</Data></Cell> </Row> <Row> <Cell><Data ss:Type="String">E</Data></Cell> <Cell><Data ss:Type="String">F</Data></Cell> </Row> <Row> <Cell><Data ss:Type="String">G</Data></Cell> <Cell><Data ss:Type="String">H</Data></Cell> </Row> </Table> </Worksheet> </Workbook>
如果来自一个数据表。
public static void DataTabletoXLS(DataTable DT, string fileName) { HttpContext.Current.Response.Clear(); HttpContext.Current.Response.Charset = "utf-16"; HttpContext.Current.Response.ContentEncoding = System.Text.Encoding.GetEncoding("windows-1250"); HttpContext.Current.Response.AddHeader("content-disposition", string.Format("attachment; filename={0}.xls", fileName)); HttpContext.Current.Response.ContentType = "application/ms-excel"; string tab = ""; foreach (DataColumn dc in DT.Columns) { HttpContext.Current.Response.Write(tab + dc.ColumnName.Replace("\n", "").Replace("\t", "")); tab = "\t"; } HttpContext.Current.Response.Write("\n"); int i; foreach (DataRow dr in DT.Rows) { tab = ""; for (i = 0; i < DT.Columns.Count; i++) { HttpContext.Current.Response.Write(tab + dr[i].ToString().Replace("\n", "").Replace("\t", "")); tab = "\t"; } HttpContext.Current.Response.Write("\n"); } HttpContext.Current.Response.End(); }
从一个GridView
public static void GridviewtoXLS(GridView gv, string fileName) { int DirtyBit = 0; int PageSize = 0; if (gv.AllowPaging == true) { DirtyBit = 1; PageSize = gv.PageSize; gv.AllowPaging = false; gv.DataBind(); } HttpContext.Current.Response.Clear(); HttpContext.Current.Response.Charset = "utf-8"; HttpContext.Current.Response.ContentEncoding = System.Text.Encoding.GetEncoding("windows-1250"); HttpContext.Current.Response.AddHeader( "content-disposition", string.Format("attachment; filename={0}.xls", fileName)); HttpContext.Current.Response.ContentType = "application/ms-excel"; using (StringWriter sw = new StringWriter()) { using (HtmlTextWriter htw = new HtmlTextWriter(sw)) { // Create a table to contain the grid Table table = new Table(); // include the gridline settings table.GridLines = gv.GridLines; // add the header row to the table if (gv.HeaderRow != null) { Utilities.Export.PrepareControlForExport(gv.HeaderRow); table.Rows.Add(gv.HeaderRow); } // add each of the data rows to the table foreach (GridViewRow row in gv.Rows) { Utilities.Export.PrepareControlForExport(row); table.Rows.Add(row); } // add the footer row to the table if (gv.FooterRow != null) { Utilities.Export.PrepareControlForExport(gv.FooterRow); table.Rows.Add(gv.FooterRow); } // render the table into the htmlwriter table.RenderControl(htw); // render the htmlwriter into the response HttpContext.Current.Response.Write(sw.ToString().Replace("£", "")); HttpContext.Current.Response.End(); } } if (DirtyBit == 1) { gv.PageSize = PageSize; gv.AllowPaging = true; gv.DataBind(); } } private static void PrepareControlForExport(Control control) { for (int i = 0; i < control.Controls.Count; i++) { Control current = control.Controls[i]; if (current is LinkButton) { control.Controls.Remove(current); control.Controls.AddAt(i, new LiteralControl((current as LinkButton).Text)); } else if (current is ImageButton) { control.Controls.Remove(current); control.Controls.AddAt(i, new LiteralControl((current as ImageButton).AlternateText)); } else if (current is HyperLink) { control.Controls.Remove(current); control.Controls.AddAt(i, new LiteralControl((current as HyperLink).Text)); } else if (current is DropDownList) { control.Controls.Remove(current); control.Controls.AddAt(i, new LiteralControl((current as DropDownList).SelectedItem.Text)); } else if (current is CheckBox) { control.Controls.Remove(current); control.Controls.AddAt(i, new LiteralControl((current as CheckBox).Checked ? "True" : "False")); } if (current.HasControls()) { Utilities.Export.PrepareControlForExport(current); } } }
这是一个免费的SpreadML封装 – 它工作的很好。
根据给出的答案和同事的咨询,似乎最好的解决办法是生成一个XML文件或HTML表格并将其作为附件推下。 我的同事推荐的一个变化是,数据(即HTML表格)可以直接写入Response对象,因此不需要写出文件,这可能会因为权限问题,I / O争夺,并确保排定清理发生。
这是代码片段…我还没有检查过,我没有提供所有的调用代码,但我认为它代表了这个想法。
Dim uiTable As HtmlTable = GetUiTable(groupedSumData) Response.Clear() Response.ContentType = "application/vnd.ms-excel" Response.AddHeader("Content-Disposition", String.Format("inline; filename=OSSummery{0:ddmmssf}.xls", DateTime.Now)) Dim writer As New System.IO.StringWriter() Dim htmlWriter As New HtmlTextWriter(writer) uiTable.RenderControl(htmlWriter) Response.Write(writer.ToString) Response.End()
由于Excel了解HTML,因此您可以将数据作为HTML表格写入具有.xls扩展名的临时文件,获取文件的FileInfo,然后使用
Response.Clear(); Response.AddHeader("Content-Disposition", "attachment; filename=" + fi.Name); Response.AddHeader("Content-Length", fi.Length.ToString()); Response.ContentType = "application/octet-stream"; Response.WriteFile(fi.FullName); Response.End();
如果您想要避免临时文件,则可以写入内存stream,然后将字节写回,而不是使用WriteFile
如果内容长度标题被省略,你可以直接写回html,但是这可能无法在所有浏览器中始终正常工作
我个人比较喜欢XML方法。 我将从数据库中的数据库返回数据,将其保存到XMl,然后创build一个xslt文件,其中包含一个将格式化正确文档的转换规则,一个简单的XML转换将完成作业。 关于这个的最好的部分,你可以格式化单元格,做条件格式,设置页眉和页脚,甚至设置打印范围。
我已经做了几次,每次最简单的方法是简单地返回一个CSV(逗号分隔值)文件。 Excel导入完美,而且做起来相对快捷。
我们始终将datagrid中的数据导出为excel。 将其转换为HTML,然后写入Excel文件
Response.ContentType = "application/vnd.ms-excel" Response.Charset = "" Response.AddHeader("content-disposition", "fileattachment;filename=YOURFILENAME.xls") Me.EnableViewState = False Dim sw As System.IO.StringWriter = New System.IO.StringWriter Dim hw As HtmlTextWriter = New HtmlTextWriter(sw) ClearControls(grid) grid.RenderControl(hw) Response.Write(sw.ToString()) Response.End()
这个方法唯一的问题是,我们很多网格中都有button或链接,所以你也需要这个:
'needed to export grid to excel to remove link button control and represent as text Private Sub ClearControls(ByVal control As Control) Dim i As Integer For i = control.Controls.Count - 1 To 0 Step -1 ClearControls(control.Controls(i)) Next i If TypeOf control Is System.Web.UI.WebControls.Image Then control.Parent.Controls.Remove(control) End If If (Not TypeOf control Is TableCell) Then If Not (control.GetType().GetProperty("SelectedItem") Is Nothing) Then Dim literal As New LiteralControl control.Parent.Controls.Add(literal) Try literal.Text = CStr(control.GetType().GetProperty("SelectedItem").GetValue(control, Nothing)) Catch End Try control.Parent.Controls.Remove(control) Else If Not (control.GetType().GetProperty("Text") Is Nothing) Then Dim literal As New LiteralControl control.Parent.Controls.Add(literal) literal.Text = CStr(control.GetType().GetProperty("Text").GetValue(control, Nothing)) control.Parent.Controls.Remove(control) End If End If End If Return End Sub
我发现在某个地方,它运作良好。
我推荐免费的基于OpenXML的开源excel生成libruary
它帮助我几个月前。
这是一个从存储过程中抽取的报告。 结果导出到Excel。 它使用ADO而不是ADO.NET,之所以是这一行
oSheet.Cells(2, 1).copyfromrecordset(rst1)
它完成了大部分的工作,在ado.net中不可用。
'Calls stored proc in SQL Server 2000 and puts data in Excel and 'formats it Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Dim cnn As ADODB.Connection cnn = New ADODB.Connection cnn.Open("Provider=SQLOLEDB;data source=xxxxxxx;" & _ "database=xxxxxxxx;Trusted_Connection=yes;") Dim cmd As New ADODB.Command cmd.ActiveConnection = cnn cmd.CommandText = "[sp_TomTepley]" cmd.CommandType = ADODB.CommandTypeEnum.adCmdStoredProc cmd.CommandTimeout = 0 cmd.Parameters.Refresh() Dim rst1 As ADODB.Recordset rst1 = New ADODB.Recordset rst1.Open(cmd) Dim oXL As New Excel.Application Dim oWB As Excel.Workbook Dim oSheet As Excel.Worksheet 'oXL = CreateObject("excel.application") oXL.Visible = True oWB = oXL.Workbooks.Add oSheet = oWB.ActiveSheet Dim Column As Integer Column = 1 Dim fld As ADODB.Field For Each fld In rst1.Fields oXL.Workbooks(1).Worksheets(1).Cells(1, Column).Value = fld.Name oXL.Workbooks(1).Worksheets(1).cells(1, Column).Interior.ColorIndex = 15 Column = Column + 1 Next fld oXL.Workbooks(1).Worksheets(1).name = "Tom Tepley Report" oSheet.Cells(2, 1).copyfromrecordset(rst1) oXL.Workbooks(1).Worksheets(1).Cells.EntireColumn.AutoFit() oXL.Visible = True oXL.UserControl = True rst1 = Nothing cnn.Close() Beep() End Sub
如果你用数据填充一个GridView,你可以使用这个函数获取HTML格式的数据,但是指出浏览器是一个excel文件。
Public Sub ExportToExcel(ByVal fileName As String, ByVal gv As GridView) HttpContext.Current.Response.Clear() HttpContext.Current.Response.AddHeader("content-disposition", String.Format("attachment; filename={0}", fileName)) HttpContext.Current.Response.ContentType = "application/ms-excel" Dim sw As StringWriter = New StringWriter Dim htw As HtmlTextWriter = New HtmlTextWriter(sw) Dim table As Table = New Table table.GridLines = gv.GridLines If (Not (gv.HeaderRow) Is Nothing) Then PrepareControlForExport(gv.HeaderRow) table.Rows.Add(gv.HeaderRow) End If For Each row As GridViewRow In gv.Rows PrepareControlForExport(row) table.Rows.Add(row) Next If (Not (gv.FooterRow) Is Nothing) Then PrepareControlForExport(gv.FooterRow) table.Rows.Add(gv.FooterRow) End If table.RenderControl(htw) HttpContext.Current.Response.Write(sw.ToString) HttpContext.Current.Response.End() End Sub Private Sub PrepareControlForExport(ByVal control As Control) Dim i As Integer = 0 Do While (i < control.Controls.Count) Dim current As Control = control.Controls(i) If (TypeOf current Is LinkButton) Then control.Controls.Remove(current) control.Controls.AddAt(i, New LiteralControl(CType(current, LinkButton).Text)) ElseIf (TypeOf current Is ImageButton) Then control.Controls.Remove(current) control.Controls.AddAt(i, New LiteralControl(CType(current, ImageButton).AlternateText)) ElseIf (TypeOf current Is HyperLink) Then control.Controls.Remove(current) control.Controls.AddAt(i, New LiteralControl(CType(current, HyperLink).Text)) ElseIf (TypeOf current Is DropDownList) Then control.Controls.Remove(current) control.Controls.AddAt(i, New LiteralControl(CType(current, DropDownList).SelectedItem.Text)) ElseIf (TypeOf current Is CheckBox) Then control.Controls.Remove(current) control.Controls.AddAt(i, New LiteralControl(CType(current, CheckBox).Checked)) End If If current.HasControls Then PrepareControlForExport(current) End If i = i + 1 Loop End Sub
只需通过Microsoft.Office.Interop命名空间避免COM Interop。 这是非常缓慢,不可靠和不可压缩的。 不适用于受虐狂。
您可以使用这个库很容易地创build格式良好的Excel文件: http : //officehelper.codeplex.com/documentation 。
Microsoft Office不需要安装在networking服务器上!
CSV是最简单的方法。 大多数情况下,它链接到Excel。 否则,您必须使用自动化API或XML格式。 API和XML不是很难使用。
有关为Excel生成XML的信息
我要么去CSV路线(如上所述),或者更多的时候,我使用Infragistics NetAdvantage生成文件。 (Infragistics在绝大多数时间都在使用,我们只是导出一个现有的UltraWebGrid,这实际上是一个LOC的解决scheme,除非需要额外的格式化调整,我们也可以手动生成一个Excel / BIFF文件,但是很less需要。)
人,在.net我想你可以有一个组件,可以做到这一点,但在经典的ASP我已经做了创build一个HTML表,并改变页面的MIME tipe到vnd / msexcel。 我猜如果你使用gridview并改变MIMEtypes也许它应该工作,因为gridview是一个HTML表。
避免“看起来像这些数字存储为文本”的绿色三angular形的唯一的防弹方法是使用Open XML格式。 值得使用它,只是为了避免不可避免的绿色三angular形。
我所见过的excel报告中最好的方法是用XML扩展名将数据写出来,并将其以正确的内容typesstream式传输给客户端。 (应用/ XLS)
这适用于任何需要基本格式化的报告,并允许您使用文本比较工具与现有电子表格进行比较。
假设这是一个Intranet,您可以在其中设置权限并强制IE,您可以使用JScript / VBScript驱动Excel来生成工作簿客户端。 这为您提供了原生的Excel格式,而无需尝试在服务器上自动化Excel。
我不确定我会不会推荐这种方法,除了在相当小的情况下,但在经典的ASP时代,这是相当普遍的。
你当然可以随时去找第三方组件。 我个人用Spire.XLS有很好的经验http://www.e-iceblue.com/xls/xlsintro.htm
该组件在您的应用程序中非常易于使用:
Workbook workbook = new Workbook(); //Load workbook from disk. workbook.LoadFromFile(@"Data\EditSheetSample.xls"); //Initailize worksheet Worksheet sheet = workbook.Worksheets[0]; //Writes string sheet.Range["B1"].Text = "Hello,World!"; //Writes number sheet.Range["B2"].NumberValue = 1234.5678; //Writes date sheet.Range["B3"].DateTimeValue = System.DateTime.Now; //Writes formula sheet.Range["B4"].Formula = "=1111*11111"; workbook.SaveToFile("Sample.xls");
我遇到的一个问题是,使用上面提到的解决scheme之一,类似于这个答案 ,如果你把内容作为附件推出(我发现它是非ms浏览器最干净的解决scheme) ,然后在Excel 2000-2003中打开它,它的types是“Excel网页”而不是本机Excel文档。
然后,您必须向用户解释如何使用Excel中的“另存为types”将其转换为Excel文档。 如果用户需要编辑此文档,然后重新上传到您的网站,这是一个痛苦。
我的build议是使用CSV。 这很简单,如果用户从Excel中打开它,Excel至less会提示他们将其保存为本机格式。
我只是基于数据创build一个CSV文件,因为我认为这是最干净的,Excel对它有很好的支持。 但是,如果你需要更灵活的格式,我肯定有一些第三方工具来生成真正的Excel文件。
这里有一个解决scheme,将数据表格以CSVformsstream出。 快速,干净,简单,它处理input中的逗号。
public static void ExportToExcel(DataTable data, HttpResponse response, string fileName) { response.Charset = "utf-8"; response.ContentEncoding = System.Text.Encoding.GetEncoding("windows-1250"); response.Cache.SetCacheability(HttpCacheability.NoCache); response.ContentType = "text/csv"; response.AddHeader("Content-Disposition", "attachment; filename=" + fileName); for (int i = 0; i < data.Columns.Count; i++) { response.Write(data.Columns[i].ColumnName); response.Write(i == data.Columns.Count - 1 ? "\n" : ","); } foreach (DataRow row in data.Rows) { for (int i = 0; i < data.Columns.Count; i++) { response.Write(String.Format("\"{0}\"", row[i].ToString())); response.Write(i == data.Columns.Count - 1 ? "\n" : ","); } } response.End(); }
只是创build了一个function,从Web窗体出口到Excel,希望它可以帮助别人
public void ExportFileFromSPData(string filename, DataTable dt) { HttpResponse response = HttpContext.Current.Response; //clean up the response.object response.Clear(); response.Buffer = true; response.Charset = ""; // set the response mime type for html so you can see what are you printing //response.ContentType = "text/html"; //response.AddHeader("Content-Disposition", "attachment;filename=test.html"); // set the response mime type for excel response.ContentType = "application/vnd.ms-excel"; response.AddHeader("Content-Disposition", "attachment;filename=\"" + filename + "\""); response.ContentEncoding = System.Text.Encoding.UTF8; response.BinaryWrite(System.Text.Encoding.UTF8.GetPreamble()); //style to format numbers to string string style = @"<style> .text { mso-number-format:\@; } </style>"; response.Write(style); // create a string writer using (StringWriter sw = new StringWriter()) { using (HtmlTextWriter htw = new HtmlTextWriter(sw)) { // instantiate a datagrid GridView dg = new GridView(); dg.DataSource = dt; dg.DataBind(); foreach (GridViewRow datarow in dg.Rows) { //format specific cell to be text //to avoid 1.232323+E29 to get 1232312312312312124124 datarow.Cells[0].Attributes.Add("class", "text"); } dg.RenderControl(htw); response.Write(sw.ToString()); response.End(); } } }
如果您必须使用Excel而不是CSV文件,则需要在服务器上的Excel实例上使用OLE自动化。 做到这一点的最简单的方法是有一个模板文件,并以编程方式填充数据。 您将其保存到另一个文件。
提示:
- 不要交互式地做。 让用户启动该进程,然后发布带有该文件链接的页面。 这可以在生成电子表格时缓解潜在的性能问题。
- 使用我之前描述的模板。 它使修改它变得更容易。
- 确保Excel设置为不popup对话框。 在Web服务器上,这将挂起整个Excel实例。
- 将Excel实例保存在单独的服务器上,最好在防火墙后面,这样就不会将其暴露为潜在的安全漏洞。
- 密切关注资源使用情况。 在OLE自动化界面上生成扩展(PIA只是简单的一个步骤)是一个相当重要的过程。 如果您需要将其扩展到较高的数据量,那么您可能需要在架构方面有点聪明。
如果你不介意这个文件的格式是有点基本的话,那么一些“使用MIMEtypes来欺骗excel打开HTML表格”的方法将会起作用。 这些方法也将CPU繁重的工作交给客户端。 如果您想对电子表格的格式进行细化控制,则可能必须使用Excel自身来生成上述的文件。