我对此进行了一些研究,但大多数解决方案都是针对 MVC 的。我只是使用 Asp.net 3.5 我如何在按钮单击时实现这一点。我是否应该包含任何库或其他任何内容。请帮忙。
3 回答
我在答案中发布的代码实际上可以在用 C# 编写的任何 ASP.NET 代码中进行任何修改而无需任何修改。助手类DataForExcel
(见文件DataForExcel.cs
)有构造函数
public DataForExcel(string[] headers, List<string[]> data, string sheetName)
或者更“高级”的版本
public DataForExcel(string[] headers, DataType[] colunmTypes,
List<string[]> data, string sheetName)
允许指定哪些列具有数字数据类型。参数List<string[]> data
是需要导出到 Excel 中的数据。该参数string[] headers
指定输出第一行的数据。
该类DataForExcel
只有一个公共方法
public void CreateXlsxAndFillData(Stream stream)
填充stream
生成的 .XLSX Excel 文件的二进制表示。
要从您的 ASP.NET 方法(例如 ASHX 处理程序)返回二进制数据,您只需执行ExecuteResult
与我的回答几乎相同的操作:
- 创建内存流
using (var stream = new MemoryStream()) {...}
- 创建
DataForExcel
导出到 Excel 所需的对象var dataExcel = new DataForExcel (new []{"Id", "Votes", "Title"}, "Questions.xlsx", "Name or Sheet");
response.AddHeader ("content-disposition", "attachment; filename=Questions.xlsx");
response.ContentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
response.ContentEncoding = Encoding.UTF8;
stream.WriteTo (response.OutputStream);
response.Flush();
就是这样。
更新:我修改了之前发布的代码,以非常简单的方式从网格中创建 Excel 文件(.xlsx 格式)。如果您使用OpenXML SDK 2.0,那么您可以使用 .NET 3.5。OpenXML SDK 2.5需要 .NET 4.0 或更高版本。
建议的代码允许将string[][]
数据转换为二进制 Excel 数据并将结果写入 Stream。您可以MemoryStream
像我之前描述的那样使用从任何 ASP.NET 应用程序返回 Excel。
建议的代码包含ExportToExcel
带有一个公共静态方法的静态类FillSpreadsheetDocument
,可以通过以下方式使用
var data = new[] {
new [] {"Tom", "30", "x", "", "1974-06-16"},
new [] {"Margarita", "34", "x", "x", "1978-10-02"},
new [] {"Bob", "7", "", "", "2005-06-26"},
new [] {"Oleg", "48", "x", "x", "1964-09-11"},
new [] {"Frank", "29", "", "x", "1983-01-28"}
};
using (var stream = new FileStream("Test.xlsx", FileMode.Create)) {
ExportToExcel.FillSpreadsheetDocument(stream,
new[] {
new ColumnModel { Type = DataType.String, Alignment = HorizontalAlignment.Left, Header = "Name" },
new ColumnModel { Type = DataType.Integer, Header = "Age" },
new ColumnModel { Type = DataType.String, Header = "Is Married", Alignment = HorizontalAlignment.Center, IsRotatedHeader = true },
new ColumnModel { Type = DataType.String, Header = "Has Children", Alignment = HorizontalAlignment.Center, IsRotatedHeader = true },
new ColumnModel { Type = DataType.Date, Header = "Birthday", Alignment = HorizontalAlignment.Left }
},
data,
"Friends");
}
它产生"Test.xlsx"
一张看起来像的“朋友”
不会设置列的宽度,但是通过两次单击(全选并在列之间双击),用户可以将列的宽度设置为最佳宽度,就像上面的构建一样。所有单元格都有格式化数据(没有“通用”格式)。我使用整数、日期和纯字符串。可以轻松创建具有居中对齐或右对齐文本的列。
您可以轻松修改代码,以便使用更多不同的文本格式。这只是一个示例如何生成带有格式化单元格的真实 Excel 文档。
您可以从此处下载正在运行的 Visual Studio 2008 项目。您将在下面找到演示的源代码:
using System;
using System.Collections.Generic;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using System.Text;
using System.IO;
using System.Globalization;
namespace ExportToExcel {
public enum ExcelCellDataType {
String,
Integer,
Date
}
public enum HorizontalAlignment {
Left,
Center,
Right
}
public class ColumnModel {
public ExcelCellDataType Type { set; get; }
public HorizontalAlignment Alignment { set; get; }
public string Header { set; get; }
public bool IsRotatedHeader { set; get; }
}
public enum OutputCellFormat: uint {
Text,
Integer,
Date,
TextHeader,
TextHeaderRotated,
TextCenter,
TextRight
}
public static class ExportToExcel {
private static StringBuilder ConvertIntToColumnHeader (uint iCol) {
var sb = new StringBuilder();
while (iCol > 0) {
if (iCol <= 'Z' - 'A') // iCol=0 -> 'A', 25 -> 'Z'
break;
sb.Append(ConvertIntToColumnHeader(iCol / ('Z' - 'A' + 1) - 1));
iCol = iCol % ('Z' - 'A' + 1);
}
sb.Append((char)('A' + iCol));
return sb;
}
private static string GetCellReference (uint iRow, uint iCol) {
return ConvertIntToColumnHeader(iCol).Append(iRow).ToString();
}
private static Row CreateColumnHeaderRow (uint iRow, IList<ColumnModel> colunmModels) {
var r = new Row { RowIndex = iRow };
for (var iCol = 0; iCol < colunmModels.Count; iCol++) {
var styleIndex = colunmModels[iCol].IsRotatedHeader
? (UInt32Value)(uint)(OutputCellFormat.TextHeaderRotated + 1)
: (UInt32Value)(uint)(OutputCellFormat.TextHeader + 1);
r.Append(new OpenXmlElement[] {
// create Cell with InlineString as a child, which has Text as a child
new Cell(new InlineString(new Text { Text = colunmModels[iCol].Header })) {
DataType = CellValues.InlineString,
StyleIndex = styleIndex,
CellReference = GetCellReference(iRow, (uint)iCol)
}
});
}
return r;
}
private static UInt32Value GetStyleIndexFromColumnModel (ColumnModel colunmModel) {
switch (colunmModel.Type) {
case ExcelCellDataType.Integer:
return (uint)(OutputCellFormat.Integer) + 1;
case ExcelCellDataType.Date:
return (uint)(OutputCellFormat.Date) + 1;
}
switch (colunmModel.Alignment) {
case HorizontalAlignment.Center:
return (uint)(OutputCellFormat.TextCenter) + 1;
case HorizontalAlignment.Right:
return (uint)(OutputCellFormat.TextRight) + 1;
default:
return (uint)(OutputCellFormat.Text) + 1;
}
}
private static string ConvertDateToString (string date) {
DateTime dt;
string text = date; // default results of conversion
if (DateTime.TryParse(date, out dt))
text = dt.ToOADate().ToString(CultureInfo.InvariantCulture);
return text;
}
private static Row CreateRow (UInt32 iRow, IList<string> data, IList<ColumnModel> colunmModels, IDictionary<string, int> sharedStrings) {
var r = new Row { RowIndex = iRow };
for (var iCol = 0; iCol < data.Count; iCol++) {
var styleIndex = (uint)(OutputCellFormat.Text) + 1;
string text = data[iCol] ?? String.Empty;
if (colunmModels != null && iCol < colunmModels.Count) {
styleIndex = GetStyleIndexFromColumnModel(colunmModels[iCol]);
switch (colunmModels[iCol].Type) {
case ExcelCellDataType.Integer:
r.Append(new OpenXmlElement[] {
// create Cell with CellValue as a child, which has Text as a child
new Cell(new CellValue { Text = text }) {
StyleIndex = styleIndex,
CellReference = GetCellReference(iRow, (uint)iCol)
}
});
continue;
case ExcelCellDataType.Date:
r.Append(new OpenXmlElement[] {
// create Cell with CellValue as a child, which has Text as a child
new Cell(new CellValue { Text = ConvertDateToString(text) }) {
StyleIndex = styleIndex,
CellReference = GetCellReference(iRow, (uint)iCol)
}
});
continue;
}
}
// default format is text
if (String.IsNullOrEmpty(text) || !sharedStrings.ContainsKey(text)) {
// create Cell with InlineString as a child, which has Text as a child
r.Append(new OpenXmlElement[] {
new Cell(new InlineString(new Text { Text = text })) {
DataType = CellValues.InlineString,
StyleIndex = styleIndex,
CellReference = GetCellReference(iRow, (uint)iCol)
}
});
} else {
r.Append(new OpenXmlElement[] {
// create Cell with CellValue as a child, which has Text as a child
new Cell(new CellValue { Text = sharedStrings[text].ToString(CultureInfo.InvariantCulture) }) {
DataType = CellValues.SharedString,
StyleIndex = styleIndex,
CellReference = GetCellReference(iRow, (uint)iCol)
}
});
}
}
return r;
}
private static void FillSpreadsheetDocument (SpreadsheetDocument spreadsheetDocument, IList<ColumnModel> columnModels, IList<string[]> data, string sheetName) {
if (columnModels == null)
throw new ArgumentNullException("columnModels");
if (data == null)
throw new ArgumentNullException("data");
// add empty workbook and worksheet to the SpreadsheetDocument
var workbookPart = spreadsheetDocument.AddWorkbookPart();
var worksheetPart = workbookPart.AddNewPart<WorksheetPart>();
var workbookStylesPart = workbookPart.AddNewPart<WorkbookStylesPart>();
// create styles for the header and columns
workbookStylesPart.Stylesheet = new Stylesheet(
new Fonts(
// Index 0 - The default font.
new Font(
new FontSize { Val = 11 },
new Color { Rgb = new HexBinaryValue { Value = "00000000" } },
new FontName { Val = "Calibri" }
),
// Index 1 - The bold font.
new Font(
new Bold(),
new FontSize { Val = 11 },
new Color { Rgb = new HexBinaryValue { Value = "00000000" } },
new FontName { Val = "Calibri" }
)
),
new Fills(
// Index 0 - required, reserved by Excel - no pattern
new Fill(new PatternFill { PatternType = PatternValues.None }),
// Index 1 - required, reserved by Excel - fill of gray 125
new Fill(new PatternFill { PatternType = PatternValues.Gray125 }),
// Index 2 - no pattern text on gray background
new Fill(new PatternFill {
PatternType = PatternValues.Solid,
BackgroundColor = new BackgroundColor { Indexed = 64U },
ForegroundColor = new ForegroundColor { Rgb = "FFD9D9D9" }
})
),
new Borders(
// Index 0 - The default border.
new Border(
new LeftBorder(),
new RightBorder(),
new TopBorder(),
new BottomBorder(),
new DiagonalBorder()
),
// Index 1 - Applies a Left, Right, Top, Bottom border to a cell
new Border(
new LeftBorder(new Color { Auto = true }) { Style = BorderStyleValues.Thin },
new RightBorder(new Color { Auto = true }) { Style = BorderStyleValues.Thin },
new TopBorder(new Color { Auto = true }) { Style = BorderStyleValues.Thin },
new BottomBorder(new Color { Auto = true }) { Style = BorderStyleValues.Thin },
new DiagonalBorder()
)
),
new CellFormats(
// Index 0 - The default cell style. If a cell does not have a style iCol applied it will use this style combination instead
new CellFormat {
NumberFormatId = (UInt32Value)0U,
FontId = (UInt32Value)0U,
FillId = (UInt32Value)0U,
BorderId = (UInt32Value)0U
},
// Index 1 - Alignment Left, Text
new CellFormat(new Alignment { Horizontal = HorizontalAlignmentValues.Left }) {
NumberFormatId = (UInt32Value)49U, // "@" - text format - see http://msdn.microsoft.com/en-us/library/documentformat.openxml.spreadsheet.numberingformat.aspx
FontId = (UInt32Value)0U,
FillId = (UInt32Value)0U,
BorderId = (UInt32Value)1U,
ApplyNumberFormat = true,
ApplyAlignment = true
},
// Index 2 - Interger Number
new CellFormat {
NumberFormatId = (UInt32Value)1U, // "0" - integer format - see http://msdn.microsoft.com/en-us/library/documentformat.openxml.spreadsheet.numberingformat.aspx
FontId = (UInt32Value)0U,
FillId = (UInt32Value)0U,
BorderId = (UInt32Value)1U,
ApplyNumberFormat = true
},
// Index 3 - Interger Date
new CellFormat {
NumberFormatId = (UInt32Value)14U, // "14" - date format mm-dd-yy - see http://msdn.microsoft.com/en-us/library/documentformat.openxml.spreadsheet.numberingformat.aspx
FontId = (UInt32Value)0U,
FillId = (UInt32Value)0U,
BorderId = (UInt32Value)1U,
ApplyNumberFormat = true
},
// Index 4 - Text for headers
new CellFormat(new Alignment {
Vertical = VerticalAlignmentValues.Center,
Horizontal = HorizontalAlignmentValues.Center
}) {
NumberFormatId = (UInt32Value)49U, // "@" - text format - see http://msdn.microsoft.com/en-us/library/documentformat.openxml.spreadsheet.numberingformat.aspx
FontId = (UInt32Value)1U,
FillId = (UInt32Value)2U,
BorderId = (UInt32Value)1U,
ApplyNumberFormat = true,
ApplyAlignment = true
},
// Index 5 - Text for headers rotated
new CellFormat(new Alignment {
Horizontal = HorizontalAlignmentValues.Center,
TextRotation = (UInt32Value)90U
}) {
NumberFormatId = (UInt32Value)49U, // "@" - text format - see http://msdn.microsoft.com/en-us/library/documentformat.openxml.spreadsheet.numberingformat.aspx
FontId = (UInt32Value)1U,
FillId = (UInt32Value)2U,
BorderId = (UInt32Value)1U,
ApplyNumberFormat = true,
ApplyAlignment = true
},
// Index 6 - Alignment Center, Text
new CellFormat(new Alignment { Horizontal = HorizontalAlignmentValues.Center }) {
NumberFormatId = (UInt32Value)49U, // "@" - text format - see http://msdn.microsoft.com/en-us/library/documentformat.openxml.spreadsheet.numberingformat.aspx
FontId = (UInt32Value)0U,
FillId = (UInt32Value)0U,
BorderId = (UInt32Value)1U,
ApplyNumberFormat = true,
ApplyAlignment = true
},
// Index 7 - Alignment Right, Text
new CellFormat(new Alignment { Horizontal = HorizontalAlignmentValues.Right }) {
NumberFormatId = (UInt32Value)49U, // "@" - text format - see http://msdn.microsoft.com/en-us/library/documentformat.openxml.spreadsheet.numberingformat.aspx
FontId = (UInt32Value)0U,
FillId = (UInt32Value)0U,
BorderId = (UInt32Value)1U,
ApplyNumberFormat = true,
ApplyAlignment = true
}
)
);
workbookStylesPart.Stylesheet.Save();
// create and fill SheetData
var sheetData = new SheetData();
// first row is the header
uint iRow = 1;
sheetData.AppendChild(CreateColumnHeaderRow(iRow++, columnModels));
//iRow++; // skip one row for the filter
// first of all collect all different strings
var sst = new SharedStringTable();
var sharedStrings = new SortedDictionary<string, int>();
foreach (var dataRow in data)
for (var iCol = 0; iCol < dataRow.Length; iCol++)
if (iCol >= columnModels.Count || columnModels[iCol].Type != ExcelCellDataType.Integer) {
string text = (columnModels[iCol].Type == ExcelCellDataType.Date
? dataRow[iCol]
: ConvertDateToString(dataRow[iCol])) ?? String.Empty;
if (!String.IsNullOrEmpty(text) && !sharedStrings.ContainsKey(text)) {
sst.AppendChild(new SharedStringItem(new Text(text)));
sharedStrings.Add(text, sharedStrings.Count);
}
}
var shareStringPart = workbookPart.AddNewPart<SharedStringTablePart>();
shareStringPart.SharedStringTable = sst;
shareStringPart.SharedStringTable.Save();
foreach (var dataRow in data)
sheetData.AppendChild(CreateRow(iRow++, dataRow, columnModels, sharedStrings));
// add sheet data to Worksheet
worksheetPart.Worksheet = new Worksheet(sheetData);
worksheetPart.Worksheet.Save();
// fill workbook with the Worksheet
spreadsheetDocument.WorkbookPart.Workbook = new Workbook(
new FileVersion { ApplicationName = "Microsoft Office Excel" },
new Sheets(new Sheet {
Name = sheetName,
SheetId = (UInt32Value)1U,
Id = workbookPart.GetIdOfPart(worksheetPart) // generate the id for sheet
})
);
spreadsheetDocument.WorkbookPart.Workbook.Save();
spreadsheetDocument.Close();
}
public static void FillSpreadsheetDocument (Stream stream, IList<ColumnModel> columnModels, IList<string[]> data, string sheetName) {
using (var spreadsheetDocument = SpreadsheetDocument.Create(stream, SpreadsheetDocumentType.Workbook)) {
FillSpreadsheetDocument(spreadsheetDocument, columnModels, data, sheetName);
}
}
}
class Program {
static void Main () {
var data = new[] {
new [] {"Tom", "30", "x", null, "1974-06-16"},
new [] {"Margarita", "34", "x", "x", null},
new [] {"Bob", "7", "", "", "2005-06-26"},
new [] {"Oleg", null, "x", "x", "1964-09-11"},
new [] {"Frank", "29", "", "x", "1983-01-28"}
};
using (var stream = new FileStream("Test.xlsx", FileMode.Create)) {
ExportToExcel.FillSpreadsheetDocument(stream,
new[] {
new ColumnModel { Type = ExcelCellDataType.String, Alignment = HorizontalAlignment.Left, Header = "Name" },
new ColumnModel { Type = ExcelCellDataType.Integer, Header = "Age" },
new ColumnModel { Type = ExcelCellDataType.String, Header = "Is Married", Alignment = HorizontalAlignment.Center, IsRotatedHeader = true },
new ColumnModel { Type = ExcelCellDataType.String, Header = "Has Children", Alignment = HorizontalAlignment.Center, IsRotatedHeader = true },
new ColumnModel { Type = ExcelCellDataType.Date, Header = "Birthday", Alignment = HorizontalAlignment.Left }
},
data,
"Friends");
}
}
}
}
虽然这是一个 PHP 解决方案,但如果有帮助的话,它的核心似乎在 Javascript 中。
您可以使用 Office XML 格式 (http://en.wikipedia.org/wiki/Microsoft_Office_XML_formats)。唯一的缺点是输出将是 XML 文件而不是二进制文件。但是,它的打开方式和行为方式与常规 .xls 文件完全相同。
我之前的做法是对Excel进行逆向工程,以我需要的格式,然后将其保存为.xml格式,在Notepad++中打开,找出页眉、表格(从网格转换而来),最后是页脚
例子:
标头(可以硬编码)
<?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>phpGrid.com</Author>
<Created></Created>
<LastSaved></LastSaved>
<Version></Version>
</DocumentProperties>
<ExcelWorkbook xmlns="urn:schemas-microsoft-com:office:excel">
<WindowHeight>768</WindowHeight>
<WindowWidth>1024</WindowWidth>
<WindowTopX>0</WindowTopX>
<WindowTopY>0</WindowTopY>
<ProtectStructure>False</ProtectStructure>
<ProtectWindows>False</ProtectWindows>
</ExcelWorkbook>
<Styles>
<Style ss:ID="Default" ss:Name="Normal">
<Alignment ss:Vertical="Bottom" />
<Borders/>
<Font ss:FontName="Arial" ss:Size="8" />
<Interior/>
<NumberFormat />
<Protection />
</Style>
<Style ss:ID="sHyperlink" ss:Name="Hyperlink">
<Font ss:Color="#0000FF" ss:Underline="Single" />
</Style>
<Style ss:ID="sDate">
<NumberFormat ss:Format="Short Date"/>
</Style>
<Style ss:ID="sNumber">
<NumberFormat/>
</Style>
<Style ss:ID="sHeader">
<Font ss:Family="Arial" ss:Bold="1" />
</Style>
<Style ss:ID="sDecimal">
<NumberFormat ss:Format="Fixed"/>
</Style>
</Styles><Worksheet ss:Name="Sheet1">
body(这个是datagrid转换出来的表格,需要动态生成)
<Table ss:ExpandedColumnCount="7"
ss:ExpandedRowCount="328" x:FullColumns="1"
x:FullRows="1"><Row><Cell ss:StyleID="sHeader"><Data ss:Type="String">Order No.</Data></Cell><Cell ss:StyleID="sHeader"><Data ss:Type="String">Order Date</Data></Cell><Cell ss:StyleID="sHeader"><Data ss:Type="String">Shipped Date</Data></Cell><Cell ss:StyleID="sHeader"><Data ss:Type="String">status</Data></Cell><Cell ss:StyleID="sHeader"><Data ss:Type="String">comments</Data></Cell><Cell ss:StyleID="sHeader"><Data ss:Type="String">Customer No.</Data></Cell></Row>
<Row><Cell ss:StyleID="sNumber"><Data ss:Type="Number">0</Data></Cell><Cell><Data ss:Type="String">2010-08-20 00:00:00</Data></Cell><Cell><Data ss:Type="String"></Data></Cell><Cell><Data ss:Type="String">Open</Data></Cell><Cell><Data ss:Type="String">foo</Data></Cell><Cell ss:StyleID="sNumber"><Data ss:Type="Number">123456</Data></Cell></Row>
<Row><Cell ss:StyleID="sNumber"><Data ss:Type="Number">10100</Data></Cell><Cell><Data ss:Type="String">2003-01-06 00:00:00</Data></Cell><Cell><Data ss:Type="String">2003-01-10 00:00:00</Data></Cell><Cell><Data ss:Type="String">Shipped</Data></Cell><Cell><Data ss:Type="String">foo</Data></Cell><Cell ss:StyleID="sNumber"><Data ss:Type="Number">363</Data></Cell></Row>
<Row><Cell ss:StyleID="sNumber"><Data ss:Type="Number">10101</Data></Cell><Cell><Data ss:Type="String">2003-01-18 00:00:00</Data></Cell><Cell><Data ss:Type="String">2003-01-22 00:00:00</Data></Cell><Cell><Data ss:Type="String">Shipped</Data></Cell><Cell><Data ss:Type="String">Check on availability.</Data></Cell><Cell ss:StyleID="sNumber"><Data ss:Type="Number">128</Data></Cell></Row></Table>
页脚(可以硬编码)
<WorksheetOptions
xmlns="urn:schemas-microsoft-com:office:excel">
<Print>
<ValidPrinterInfo />
<HorizontalResolution>800</HorizontalResolution>
<VerticalResolution>0</VerticalResolution>
</Print>
<Selected />
<Panes>
<Pane>
<Number>3</Number>
<ActiveRow>1</ActiveRow>
</Pane>
</Panes>
<ProtectObjects>False</ProtectObjects>
<ProtectScenarios>False</ProtectScenarios>
</WorksheetOptions>
</Worksheet>
</Workbook>
您可以简单地对页眉和页脚 XML 进行硬编码,只会动态生成正文。如果您需要对齐和其他所需的样式。再次尝试相同的“逆向”工程方法并找出需要添加到 XML 中的内容。
我发现这种方式的一个优点是您不需要依赖 API,因为实际上所有内容都可以混合成一个字符串。它也适用于不同的编程语言。可能不如 API 灵活或强大,但对于 datagrid,只有表、行和列就足够了。至少对我来说。