I’ve seen Excel used for a ton of different solutions; Flow Charts, Business Forms, EDI, Photo Album, etc… Regardless of how Excel is being used, it’s one of the most common business document formats I know. We commonly refer to Excel as duct tape, in the office. I’ve traditionally limited my web & Excel integration to the capabilities of the System.Data.OleDb namespace. This has been a great solution, and when you can’t buy 3rd party controls or run code in full trust, it’s a proven solution. This has solved 99% of my problems, but there’s always been a need to do a bit more than use Excel as a basic data source. Fortunate for me, I found a trail of posts in StackOverflow that lead me to NPOI. It is a .NET implementation of the Apache POI Project. NPOI is a bit behind POI, but this amazing library gives you the ability to develop rich Excel with a easy to use API.
I’ve run into a few bugs since starting, but I found lots of posts on CodePlex where the project is hosted. I’ve found that running 1.2.2 (alpha) solves a bunch of bugs I immediately found when running 1.2.1. I downloaded the latest build from CodePlex, but it was still on a 1.2.1 branch and was missing some of the 1.2.2 fixes.
Now that we know what NPOI is going to provide us, lets go grab the files we’ll need to get started.
- NPOI.Util Basic assistant class library
- NPOI.POIFS OLE2 format read/write library
- NPOI.DDF Drawing format read/write library
- NPOI.SS Formula evaluation library
- NPOI.HPSF Summary Information and Document Summary Information read/write library
- NPOI.HSSF Excel BIFF format read/write library
To keep the demo easy to follow and provide some good ways to refactor NPOI into your existing project, I’ve taken a old function that would output a DataTable to a HTML formatted document with an Excel (xls) extension. This solution has worked for years, but every time you open one of these XLS files you get the following message (“The file you are trying to open “”, is in a different format than specified by the file extension. Verify that the file is not corrupted and is from a trusted source before opening the file. Do you want to open the file now?“). It’s a bit annoying and scary to the user, but this approach was used to solve the problem of Excel truncating leading zero’s in numeric data (e.g. 00002345).
Before NPOI Function (DataTable to HTML document saved with a XLS extension)
/// <summary>
/// Exports a DataTable as a HTML formatted table and forces the results to be downloaded in a .HTM file.
/// </summary>
/// <param name="dataTable">DataTable</param>
/// <param name="fileName">Output File Name</param>
static public void DataTableToHtmlXLS(DataTable dataTable, string fileName)
{
//Add _response header
HttpResponse response = HttpContext.Current.Response;
response.Clear();
response.ClearHeaders();
response.AddHeader("content-disposition", string.Format("attachment;filename={0}.xls", fileName));
response.ContentEncoding = Encoding.Default;
response.Charset = String.Empty;
response.ContentType = "text/HTML";
DataGrid dataGrid = new DataGrid();
dataGrid.EnableViewState = false;
dataGrid.DataSource = dataTable;
dataGrid.DataBind();
StringWriter sw = new StringWriter();
HtmlTextWriter hw = new HtmlTextWriter(sw);
dataGrid.RenderControl(hw);
response.Write(sw.ToString());
response.End();
}
** If you were to look at the the XLS file in notepad, you’d see the contents are HTML.
NPOI Function (DataTable to Excel)
/// <summary>
/// Render DataTable to Excel File
/// </summary>
/// <param name="sourceTable">Source DataTable</param>
/// <param name="fileName">Destination File name</param>
public static void ExportDataTableToExcel(DataTable sourceTable, string fileName)
{
HSSFWorkbook workbook = new HSSFWorkbook();
MemoryStream memoryStream = new MemoryStream();
HSSFSheet sheet = workbook.CreateSheet("Sheet1");
HSSFRow headerRow = sheet.CreateRow(0);
// handling header.
foreach (DataColumn column in sourceTable.Columns)
headerRow.CreateCell(column.Ordinal).SetCellValue(column.ColumnName);
// handling value.
int rowIndex = 1;
foreach (DataRow row in sourceTable.Rows)
{
HSSFRow dataRow = sheet.CreateRow(rowIndex);
foreach (DataColumn column in sourceTable.Columns)
{
dataRow.CreateCell(column.Ordinal).SetCellValue(row[column].ToString());
}
rowIndex++;
}
workbook.Write(memoryStream);
memoryStream.Flush();
HttpResponse response = HttpContext.Current.Response;
response.ContentType = "application/vnd.ms-excel";
response.AddHeader("Content-Disposition", string.Format("attachment;filename={0}", fileName));
response.Clear();
response.BinaryWrite(memoryStream.GetBuffer());
response.End();
}
** Pretty simple right? In my production code this is actually split into multiple functions so I can reuse the DataTable to Excel code. The output of this function is a Excel 2003 (XLS) BIFF.
Now that we are using NPOI to create “real” XLS documents, we can do something cool like add formatting to our header row.
// handling header.
foreach (DataColumn column in sourceTable.Columns)
{
// Create New Cell
HSSFCell headerCell = headerRow.CreateCell(column.Ordinal);
// Set Cell Value
headerCell.SetCellValue(column.ColumnName);
// Create Style
HSSFCellStyle headerCellStyle = workbook.CreateCellStyle();
headerCellStyle.FillForegroundColor = HSSFColor.AQUA.index;
headerCellStyle.FillPattern = CellFillPattern.SOLID_FOREGROUND;
// Add Style to Cell
headerCell.CellStyle = headerCellStyle;
}
There is a million more formatting options possible and by the time you read this you should find a few more example on my site.
#1 by Avinesh on June 18, 2011 - 11:35 pm
Hi,
I just starting using NPOI to build a .net application that can export data to excel and it is working well. However now I would like to increase the application’s performance by decreasing the actual runtime, by making use of multi-core processing (or parallel processing) to get the full usage of the processing power and decrease the conversion time.
Do you have any suggestion with regards to this using NPOI?
Thanks in advance,
#2 by Jason ⋆★ 'Loki '★⋆ Smith on May 13, 2011 - 6:25 am
awesome post zach!!!!!!!!!!
thanx a million!
#3 by mkk on May 3, 2011 - 9:46 am
Hi Zach,
This was a nice blog post, is there someway that i want to save the file in the server, and directly to the browser
#4 by Zach on May 24, 2011 - 3:34 pm
Yes… You can save it to the server by changing the “Write” method to use a file stream that points to a location on your server.
#5 by Zach on April 4, 2011 - 2:24 pm
Tim,
Yes, you can definitely add more sheets… I have an example in one of my other posts about automatically starting a new sheet when you hit 65.5K row limit. In a nutshell, you need to track your row count and once you hit the limit you create a new sheet and continue loading. If you need to work with lots of rows and your using Office 2007+ you should consider XLSX since the limit was increased to 1M rows.
Zach
#6 by Tim Larkin on April 4, 2011 - 9:35 am
Hi Zach.
I am running into a problem. I wrote a C# console app that processes dozens of XML files, one at a time and writes them out as .XLS files. The issue I am not running into is this. Some of the XML files are hitting 400Mbytes in size. Which means I am hitting some memory issue, but the issue I am primarily concerned with is I am not hitting files with multiple sheets some of which contain as many as 100 thousand Rows so I am getting and int16 based error. Is there anyway to A). Append sheets to an existing xls file.
and B) have you are anyone you know of recoded or changed the library to use int 32 for more records?
#7 by Macsys on January 24, 2011 - 3:42 am
I was just looking for something like this to export my data table to Excel in BIFF format. It worked great.
One thing. Can you suggest something similar which produces .XLSX (2007 Format) files.
Thanks