Существует хорошая библиотека для Java Apache POI — the Java API for Microsoft Documents http://poi.apache.org/, которая позволяет работать с документами Microsoft. Была создана портация этой библиотеки на C# и называется она NPOI http://npoi.codeplex.com/. В данной заметке будет описан пример использования NPOI.
Для получения библиотеки идём на сайт https://npoi.codeplex.com/ переходим в раздел Downloads и скачиваем последнюю версию, на момент написания заметки это 2.1.3.1. Разархивируем архив папку с библиотеками.
В архиве есть библиотеки для версии Net 4.0 и Net 2.0.
Теперь создаём тестовый проект в VisulaStudio. Назовём его к примеру TestNPOI.
Добавим на форму 2 текстовых окна для добавления файлов и две кнопки.
В Referens проекта добавим скачанные библиотеки, в моём случае я добавлял библиотеки для Net 4.0.
Напишем код для сохранения файла в Excel в формат xlsx. К нашему модулю нужно подключить стандартные модули и два модуля using NPOI.SS.UserModel; и NPOI NPOI.XSSF.UserModel:
using System.IO;
using System.Windows;
using System.Diagnostics;
using NPOI.SS.UserModel;
using NPOI.XSSF.UserModel;
Теперь напишем код по выгрузке данных в Excel файл.
private void bSaveFile_Click(object sender, RoutedEventArgs e)
{
//Рабочая книга Excel
XSSFWorkbook wb;
//Лист в книге Excel
XSSFSheet sh;
//Создаем рабочую книгу
wb = new XSSFWorkbook();
//Создаём лист в книге
sh = (XSSFSheet)wb.CreateSheet("Лист 1");
//Количество заполняемых строк
int countRow = 3;
//Количество заполняемых столбцов
int countColumn = 3;
//Запускаем цыкл по строка
for (int i = 0; i < countRow; i++)
{
//Создаем строку
var currentRow = sh.CreateRow(i);
//Запускаем цикл по столбцам
for (int j = 0; j < countColumn; j++)
{
//в строке создаём ячеёку с указанием столбца
var currentCell = currentRow.CreateCell(j);
//в ячейку запишем информацию о текущем столбце и строке
currentCell.SetCellValue("Строка - "+(i+1).ToString()+"Столбец - "+(j+1).ToString());
//Выравним размер столбца по содержимому
sh.AutoSizeColumn(j);
}
}
// Удалим файл если он есть уже
if (!File.Exists(tbSaveFile.Text))
{
File.Delete(tbSaveFile.Text);
}
//запишем всё в файл
using (var fs = new FileStream(tbSaveFile.Text, FileMode.Create, FileAccess.Write))
{
wb.Write(fs);
}
//Откроем файл
Process.Start(tbSaveFile.Text);
}
В результате получи следующий файл
Теперь сохраним этот файл в TestLoad.xlsx и напишем код по чтению данных из файла.
private void bLoadFile_Click(object sender, RoutedEventArgs e)
{
//Книга Excel
XSSFWorkbook xssfwb;
//Открываем файл
using (FileStream file = new FileStream(tbLoadFile.Text, FileMode.Open, FileAccess.Read))
{
xssfwb = new XSSFWorkbook(file);
}
//Получаем первый лист книги
ISheet sheet = xssfwb.GetSheetAt(0);
//запускаем цикл по строкам
for (int row = 0; row <= sheet.LastRowNum; row++)
{
//получаем строку
var currentRow = sheet.GetRow(row);
if (currentRow != null) //null когда строка содержит только пустые ячейки
{
//запускаем цикл по столбцам
for (int column = 0; column < 3; column++)
{
//получаем значение яейки
var stringCellValue = currentRow.GetCell(column).StringCellValue;
//Выводим сообщение
MessageBox.Show(string.Format("Ячейка {0}-{1} значение:{2}", row,column,stringCellValue));
}
}
}
}
NPOI Examples
This repository is splitted from NPOI master repository in order to manage the examples easily.
About Donation
Since Github sponsorship is not supported in China so far, it’s hard to get donation from Github channel. I’m strictly limiting my contribution time on NPOI these 2 years although it looks to be still maintained well. If you profits/benefits from NPOI and you believe it’s useful, please donate this project. Thank you!
Telegram User Group
Join us on telegram: https://t.me/npoidevs
To Get Started with NPOI (on Windows 10)
- Open PowerShell and run the following command:
git clone https://github.com/nissl-lab/npoi-examples
- Open npoi-examples/ss/CalendarDemo/CalendarDemo.csproj with Visual Studio 2019 community version and click ‘Run program’ button
- You will see a new generated file called Calendar.xls under npoi-examples/ss/CalendarDemo/bin/Debug folder
- Open the Calendar.xls with Microsoft Excel or Kingsoft WPS
- Go back to Powershell window and run the following command
cd npoi-examples/ss/CalendarDemo/bin/Debug
./CalendarDemo -xlsx
- You will see a new generated file called Calendar.xlsx under npoi-examples/ss/CalendarDemo/bin/Debug folder
- Open the Calendar.xlsx with Microsoft Excel or Kingsoft WPS
Conclusion: The result of Calendar.xls and Calendar.xlsx looks same in Microsoft Excel or Kingsoft WPS but they are totally different file formats generated by NPOI.
Folders Explained
Folder Name | Description |
---|---|
POIFS | OLE2/ActiveX document examples |
HSSF | examples for Microsoft Excel BIFF(Excel 97-2003, xls) |
SS | Excel Common examples for both Excel 2003(xls) and Excel 2007+(xlsx) |
XSSF | Excel 2007(xlsx) examples |
XWPF | Word 2007(docx) examples |
OOXML | OpenXml format low-level examples |
ScratchPad/HWPF | Word 2003(doc) examples |
I find NPOI very usefull for working with Excel Files, here is my implementation (Comments are in Spanish, sorry for that):
This Method Opens an Excel (both xls or xlsx) file and converts it into a DataTable.
/// <summary>Abre un archivo de Excel (xls o xlsx) y lo convierte en un DataTable.
/// LA PRIMERA FILA DEBE CONTENER LOS NOMBRES DE LOS CAMPOS.</summary>
/// <param name="pRutaArchivo">Ruta completa del archivo a abrir.</param>
/// <param name="pHojaIndex">Número (basado en cero) de la hoja que se desea abrir. 0 es la primera hoja.</param>
private DataTable Excel_To_DataTable(string pRutaArchivo, int pHojaIndex)
{
// --------------------------------- //
/* REFERENCIAS:
* NPOI.dll
* NPOI.OOXML.dll
* NPOI.OpenXml4Net.dll */
// --------------------------------- //
/* USING:
* using NPOI.SS.UserModel;
* using NPOI.HSSF.UserModel;
* using NPOI.XSSF.UserModel; */
// AUTOR: Ing. Jhollman Chacon R. 2015
// --------------------------------- //
DataTable Tabla = null;
try
{
if (System.IO.File.Exists(pRutaArchivo))
{
IWorkbook workbook = null; //IWorkbook determina si es xls o xlsx
ISheet worksheet = null;
string first_sheet_name = "";
using (FileStream FS = new FileStream(pRutaArchivo, FileMode.Open, FileAccess.Read))
{
workbook = WorkbookFactory.Create(FS); //Abre tanto XLS como XLSX
worksheet = workbook.GetSheetAt(pHojaIndex); //Obtener Hoja por indice
first_sheet_name = worksheet.SheetName; //Obtener el nombre de la Hoja
Tabla = new DataTable(first_sheet_name);
Tabla.Rows.Clear();
Tabla.Columns.Clear();
// Leer Fila por fila desde la primera
for (int rowIndex = 0; rowIndex <= worksheet.LastRowNum; rowIndex++)
{
DataRow NewReg = null;
IRow row = worksheet.GetRow(rowIndex);
IRow row2 = null;
IRow row3 = null;
if (rowIndex == 0)
{
row2 = worksheet.GetRow(rowIndex + 1); //Si es la Primera fila, obtengo tambien la segunda para saber el tipo de datos
row3 = worksheet.GetRow(rowIndex + 2); //Y la tercera tambien por las dudas
}
if (row != null) //null is when the row only contains empty cells
{
if (rowIndex > 0) NewReg = Tabla.NewRow();
int colIndex = 0;
//Leer cada Columna de la fila
foreach (ICell cell in row.Cells)
{
object valorCell = null;
string cellType = "";
string[] cellType2 = new string[2];
if (rowIndex == 0) //Asumo que la primera fila contiene los titlos:
{
for (int i = 0; i < 2; i++)
{
ICell cell2 = null;
if (i == 0) { cell2 = row2.GetCell(cell.ColumnIndex); }
else { cell2 = row3.GetCell(cell.ColumnIndex); }
if (cell2 != null)
{
switch (cell2.CellType)
{
case CellType.Blank: break;
case CellType.Boolean: cellType2[i] = "System.Boolean"; break;
case CellType.String: cellType2[i] = "System.String"; break;
case CellType.Numeric:
if (HSSFDateUtil.IsCellDateFormatted(cell2)) { cellType2[i] = "System.DateTime"; }
else
{
cellType2[i] = "System.Double"; //valorCell = cell2.NumericCellValue;
}
break;
case CellType.Formula:
bool continuar = true;
switch (cell2.CachedFormulaResultType)
{
case CellType.Boolean: cellType2[i] = "System.Boolean"; break;
case CellType.String: cellType2[i] = "System.String"; break;
case CellType.Numeric:
if (HSSFDateUtil.IsCellDateFormatted(cell2)) { cellType2[i] = "System.DateTime"; }
else
{
try
{
//DETERMINAR SI ES BOOLEANO
if (cell2.CellFormula == "TRUE()") { cellType2[i] = "System.Boolean"; continuar = false; }
if (continuar && cell2.CellFormula == "FALSE()") { cellType2[i] = "System.Boolean"; continuar = false; }
if (continuar) { cellType2[i] = "System.Double"; continuar = false; }
}
catch { }
} break;
}
break;
default:
cellType2[i] = "System.String"; break;
}
}
}
//Resolver las diferencias de Tipos
if (cellType2[0] == cellType2[1]) { cellType = cellType2[0]; }
else
{
if (cellType2[0] == null) cellType = cellType2[1];
if (cellType2[1] == null) cellType = cellType2[0];
if (cellType == "") cellType = "System.String";
}
//Obtener el nombre de la Columna
string colName = "Column_{0}";
try { colName = cell.StringCellValue; }
catch { colName = string.Format(colName, colIndex); }
//Verificar que NO se repita el Nombre de la Columna
foreach (DataColumn col in Tabla.Columns)
{
if (col.ColumnName == colName) colName = string.Format("{0}_{1}", colName, colIndex);
}
//Agregar el campos de la tabla:
DataColumn codigo = new DataColumn(colName, System.Type.GetType(cellType));
Tabla.Columns.Add(codigo); colIndex++;
}
else
{
//Las demas filas son registros:
switch (cell.CellType)
{
case CellType.Blank: valorCell = DBNull.Value; break;
case CellType.Boolean: valorCell = cell.BooleanCellValue; break;
case CellType.String: valorCell = cell.StringCellValue; break;
case CellType.Numeric:
if (HSSFDateUtil.IsCellDateFormatted(cell)) { valorCell = cell.DateCellValue; }
else { valorCell = cell.NumericCellValue; } break;
case CellType.Formula:
switch (cell.CachedFormulaResultType)
{
case CellType.Blank: valorCell = DBNull.Value; break;
case CellType.String: valorCell = cell.StringCellValue; break;
case CellType.Boolean: valorCell = cell.BooleanCellValue; break;
case CellType.Numeric:
if (HSSFDateUtil.IsCellDateFormatted(cell)) { valorCell = cell.DateCellValue; }
else { valorCell = cell.NumericCellValue; }
break;
}
break;
default: valorCell = cell.StringCellValue; break;
}
//Agregar el nuevo Registro
if (cell.ColumnIndex <= Tabla.Columns.Count - 1) NewReg[cell.ColumnIndex] = valorCell;
}
}
}
if (rowIndex > 0) Tabla.Rows.Add(NewReg);
}
Tabla.AcceptChanges();
}
}
else
{
throw new Exception("ERROR 404: El archivo especificado NO existe.");
}
}
catch (Exception ex)
{
throw ex;
}
return Tabla;
}
This Second method does the oposite, saves a DataTable into an Excel File, yeah it can either be xls or the new xlsx, your choise!
/// <summary>Convierte un DataTable en un archivo de Excel (xls o Xlsx) y lo guarda en disco.</summary>
/// <param name="pDatos">Datos de la Tabla a guardar. Usa el nombre de la tabla como nombre de la Hoja</param>
/// <param name="pFilePath">Ruta del archivo donde se guarda.</param>
private void DataTable_To_Excel(DataTable pDatos, string pFilePath)
{
try
{
if (pDatos != null && pDatos.Rows.Count > 0)
{
IWorkbook workbook = null;
ISheet worksheet = null;
using (FileStream stream = new FileStream(pFilePath, FileMode.Create, FileAccess.ReadWrite))
{
string Ext = System.IO.Path.GetExtension(pFilePath); //<-Extension del archivo
switch (Ext.ToLower())
{
case ".xls":
HSSFWorkbook workbookH = new HSSFWorkbook();
NPOI.HPSF.DocumentSummaryInformation dsi = NPOI.HPSF.PropertySetFactory.CreateDocumentSummaryInformation();
dsi.Company = "Cutcsa"; dsi.Manager = "Departamento Informatico";
workbookH.DocumentSummaryInformation = dsi;
workbook = workbookH;
break;
case ".xlsx": workbook = new XSSFWorkbook(); break;
}
worksheet = workbook.CreateSheet(pDatos.TableName); //<-Usa el nombre de la tabla como nombre de la Hoja
//CREAR EN LA PRIMERA FILA LOS TITULOS DE LAS COLUMNAS
int iRow = 0;
if (pDatos.Columns.Count > 0)
{
int iCol = 0;
IRow fila = worksheet.CreateRow(iRow);
foreach (DataColumn columna in pDatos.Columns)
{
ICell cell = fila.CreateCell(iCol, CellType.String);
cell.SetCellValue(columna.ColumnName);
iCol++;
}
iRow++;
}
//FORMATOS PARA CIERTOS TIPOS DE DATOS
ICellStyle _doubleCellStyle = workbook.CreateCellStyle();
_doubleCellStyle.DataFormat = workbook.CreateDataFormat().GetFormat("#,##0.###");
ICellStyle _intCellStyle = workbook.CreateCellStyle();
_intCellStyle.DataFormat = workbook.CreateDataFormat().GetFormat("#,##0");
ICellStyle _boolCellStyle = workbook.CreateCellStyle();
_boolCellStyle.DataFormat = workbook.CreateDataFormat().GetFormat("BOOLEAN");
ICellStyle _dateCellStyle = workbook.CreateCellStyle();
_dateCellStyle.DataFormat = workbook.CreateDataFormat().GetFormat("dd-MM-yyyy");
ICellStyle _dateTimeCellStyle = workbook.CreateCellStyle();
_dateTimeCellStyle.DataFormat = workbook.CreateDataFormat().GetFormat("dd-MM-yyyy HH:mm:ss");
//AHORA CREAR UNA FILA POR CADA REGISTRO DE LA TABLA
foreach (DataRow row in pDatos.Rows)
{
IRow fila = worksheet.CreateRow(iRow);
int iCol = 0;
foreach (DataColumn column in pDatos.Columns)
{
ICell cell = null; //<-Representa la celda actual
object cellValue = row[iCol]; //<- El valor actual de la celda
switch (column.DataType.ToString())
{
case "System.Boolean":
if (cellValue != DBNull.Value)
{
cell = fila.CreateCell(iCol, CellType.Boolean);
if (Convert.ToBoolean(cellValue)) { cell.SetCellFormula("TRUE()"); }
else { cell.SetCellFormula("FALSE()"); }
cell.CellStyle = _boolCellStyle;
}
break;
case "System.String":
if (cellValue != DBNull.Value)
{
cell = fila.CreateCell(iCol, CellType.String);
cell.SetCellValue(Convert.ToString(cellValue));
}
break;
case "System.Int32":
if (cellValue != DBNull.Value)
{
cell = fila.CreateCell(iCol, CellType.Numeric);
cell.SetCellValue(Convert.ToInt32(cellValue));
cell.CellStyle = _intCellStyle;
}
break;
case "System.Int64":
if (cellValue != DBNull.Value)
{
cell = fila.CreateCell(iCol, CellType.Numeric);
cell.SetCellValue(Convert.ToInt64(cellValue));
cell.CellStyle = _intCellStyle;
}
break;
case "System.Decimal":
if (cellValue != DBNull.Value)
{
cell = fila.CreateCell(iCol, CellType.Numeric);
cell.SetCellValue(Convert.ToDouble(cellValue));
cell.CellStyle = _doubleCellStyle;
}
break;
case "System.Double":
if (cellValue != DBNull.Value)
{
cell = fila.CreateCell(iCol, CellType.Numeric);
cell.SetCellValue(Convert.ToDouble(cellValue));
cell.CellStyle = _doubleCellStyle;
}
break;
case "System.DateTime":
if (cellValue != DBNull.Value)
{
cell = fila.CreateCell(iCol, CellType.Numeric);
cell.SetCellValue(Convert.ToDateTime(cellValue));
//Si No tiene valor de Hora, usar formato dd-MM-yyyy
DateTime cDate = Convert.ToDateTime(cellValue);
if (cDate != null && cDate.Hour > 0) { cell.CellStyle = _dateTimeCellStyle; }
else { cell.CellStyle = _dateCellStyle; }
}
break;
default:
break;
}
iCol++;
}
iRow++;
}
workbook.Write(stream);
stream.Close();
}
}
}
catch (Exception ex)
{
throw ex;
}
}
With this 2 methods you can Open an Excel file, load it into a DataTable, do your modifications and save it back into an Excel file.
Hope you guys find this usefull.
M.T
Posted on Jan 29, 2020
Generating an Excel based on some data-set is one of these tasks that you have to do from time to time as a developer.
Normally, I don’t get much of these scenarios when I have to generate an excel, and when I do, I almost forgetting about how is done partially at least.
So, I’ve decided to document a sample and share it out, as it might help someone out there to refresh the vagueness.
Without further due, here we go:
Obviously, the first step would be installing the NPOI library using NuGet which is straight forward process.
NPOI consist of many namespaces, but for now our focus would be on only two:
using NPOI.HSSF.UserModel;
using NPOI.SS.UserModel;
We will need NPOI.HSSF.UserModel
to be able to use the HSSFWorkbook
,HSSFFont
, HSSFCellStyle
and others needed objects.
While using NPOI.SS.UserModel;
will be used to define ISheet
,IRow
,ICell
and other required objects.
The Logic of creating an excel is simple:
- Define a Workbook.
- Create a Sheet to the workbook.
- Add Rows and Cells to the Sheet.
Now before I dive into creating rows & cells, I’ll just write a function to create a cell for us, so instead of writing
ICell Cell = CurrentRow.CreateCell(CellIndex);
Cell.SetCellValue(Value);
everytime we need to create a cell, we just create this function:
private void CreateCell(IRow CurrentRow, int CellIndex, string Value, HSSFCellStyle Style)
{
ICell Cell = CurrentRow.CreateCell(CellIndex);
Cell.SetCellValue(Value);
Cell.CellStyle = Style;
}
So, creating a cell now is just:
CreateCell(HeaderRow, CellIndex, "Column Value", CellStyle);
Now, let’s start creating an excel based on defined headers and given data collection.
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFFont myFont = (HSSFFont)workbook.CreateFont();
myFont.FontHeightInPoints = 11;
myFont.FontName = "Tahoma";
// Defining a border
HSSFCellStyle borderedCellStyle = (HSSFCellStyle)workbook.CreateCellStyle();
borderedCellStyle.SetFont(myFont);
borderedCellStyle.BorderLeft = BorderStyle.Medium;
borderedCellStyle.BorderTop = BorderStyle.Medium;
borderedCellStyle.BorderRight = BorderStyle.Medium;
borderedCellStyle.BorderBottom = BorderStyle.Medium;
borderedCellStyle.VerticalAlignment = VerticalAlignment.Center;
ISheet Sheet = workbook.CreateSheet("Report");
//Creat The Headers of the excel
IRow HeaderRow = Sheet.CreateRow(0);
//Create The Actual Cells
CreateCell(HeaderRow, 0, "Batch Name", borderedCellStyle);
CreateCell(HeaderRow, 1, "RuleID", borderedCellStyle);
CreateCell(HeaderRow, 2, "Rule Type", borderedCellStyle);
CreateCell(HeaderRow, 3, "Code Message Type", borderedCellStyle);
CreateCell(HeaderRow, 4, "Severity", borderedCellStyle);
// This Where the Data row starts from
int RowIndex = 1;
//Iteration through some collection
foreach (BatchErrorReport batchErrorReport in BatchErrorReports)
{
//Creating the CurrentDataRow
IRow CurrentRow = Sheet.CreateRow(RowIndex);
CreateCell(CurrentRow, 0, batchErrorReport.Name, borderedCellStyle);
// This will be used to calculate the merge area
int NumberOfRules = batchErrorReport.Rules.Count;
if (NumberOfRules > 1)
{
int MergeIndex = (NumberOfRules - 1) + RowIndex;
//Merging Cells
NPOI.SS.Util.CellRangeAddress MergedBatch = new NPOI.SS.Util.CellRangeAddress(RowIndex, MergeIndex, 0, 0);
Sheet.AddMergedRegion(MergedBatch);
}
int i = 0;
// Iterate through cub collection
foreach (BatchDataQuality batchDataQuality in batchErrorReport.Rules)
{
if (i > 0)
CurrentRow = Sheet.CreateRow(RowIndex);
CreateCell(CurrentRow, 1, batchDataQuality.RuleID, borderedCellStyle);
CreateCell(CurrentRow, 2, batchDataQuality.RuleType, borderedCellStyle);
CreateCell(CurrentRow, 3, batchDataQuality.CodeMessageType, borderedCellStyle);
CreateCell(CurrentRow, 4, batchDataQuality.Severity, borderedCellStyle);
RowIndex++;
i++;
}
RowIndex = NumberOfRules >= 1 ? RowIndex : RowIndex + 1;
}
// Auto sized all the affected columns
int lastColumNum = Sheet.GetRow(0).LastCellNum;
for (int i = 0; i <= lastColumNum; i++)
{
Sheet.AutoSizeColumn(i);
GC.Collect();
}
// Write Excel to disk
using (var fileData = new FileStream(Utility.DOCUMENT_PATH + "ReportName.xls", FileMode.Create))
{
workbook.Write(fileData);
}
This would produce something like this:
- Download sample — 650.6 KB
Introduction
This article will walk you through the generic export to Excel feature which is tried and well tested, using C# in WEB API and by using NPOI.
This article is specific to .NET Framework, whereas, I have tried in .NET Core, it works perfectly fine. So hold on and let’s jump over it.
Before We Begin the Journey
The main concept of this article is to develop generic Excel to export functionality by importing NPOI and add the below explained 2 cs files included in the sample and then jet set go.
Explore the Code
I have created a GitHub repository for you to explore the code with ASP.NET MVC, which is right here:
- https://github.com/ansaridawood/.NET-Generic-Excel-Export-Sample
Background
We are using NPOI DLL for this export which is free to use, refer to NPOI NuGet for more details.
More often, we need to develop an export to Excel feature in our applications, many of us usually create boring string
builder, then convert it to Excel or use Interop or ITextSharp or NPOI or something else to achieve the same result.
All the above-listed ways are absolutely correct in their own context and fine, but what if there could be a way to export to Excel as simple as passing an object and getting the required output, our life would have been easier, isn’t it?
This is what I am going to explain in this article.
Using the Code
First of all, there is a utility function called Export()
which simply converts your C# List
object to NPOI object and then finally provides to HttpResponseMessage
type, which can be used in your WEB API Action.
You need 2 files to achieve it — refer to the solution attached in this article for a better understanding >> ExcelExport folder in Root directory:
- AbstractDataExport.cs — contains common code
- AbstractDataExportBridge.cs — converts
List
to NPOI Excel object
What does AbstractDataExport.cs do?
Refer to Export(List exportData, string fileName, string sheetName = DefaultSheetName)
Let’s begin with our first file, AbstractDataExport.cs
Creating a New Excel object — _workbook = new XSSFWorkbook();
- Creating a New Excel Sheet object —
_sheet = _workbook.CreateSheet(_sheetName);
- Invokes
WriteData()
— explained later - Finally, creating and returning
MemoryStream
object
using NPOI.SS.UserModel; using NPOI.XSSF.UserModel; using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Http; using System.Net.Http.Headers; namespace GenericExcelExport.ExcelExport { public interface IAbstractDataExport { HttpResponseMessage Export(List exportData, string fileName, string sheetName); } public abstract class AbstractDataExport : IAbstractDataExport { protected string _sheetName; protected string _fileName; protected List _headers; protected List _type; protected IWorkbook _workbook; protected ISheet _sheet; private const string DefaultSheetName = "Sheet1"; public HttpResponseMessage Export (List exportData, string fileName, string sheetName = DefaultSheetName) { _fileName = fileName; _sheetName = sheetName; _workbook = new XSSFWorkbook(); _sheet = _workbook.CreateSheet(_sheetName); var headerStyle = _workbook.CreateCellStyle(); var headerFont = _workbook.CreateFont(); headerFont.IsBold = true; headerStyle.SetFont(headerFont); WriteData(exportData); var header = _sheet.CreateRow(0); for (var i = 0; i < _headers.Count; i++) { var cell = header.CreateCell(i); cell.SetCellValue(_headers[i]); cell.CellStyle = headerStyle; } using (var memoryStream = new MemoryStream()) { _workbook.Write(memoryStream); var response = new HttpResponseMessage(HttpStatusCode.OK) { Content = new ByteArrayContent(memoryStream.ToArray()) }; response.Content.Headers.ContentType = new MediaTypeHeaderValue ("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment") { FileName = $"{_fileName}_{DateTime.Now.ToString("yyyyMMddHHmmss")}.xlsx" }; return response; } } public abstract void WriteData(List exportData); } }
Now, let’s proceed towards our second and final file, i.e., AbstractDataExportBridge.cs. Below is the explanation for WriteData(List exportData)
:
- Converts
List
toDataTable
Reflection
to read property name, your column header will be coming from here- Loop through
DataTable
to create Excel Rows
There are areas of improvement here, you can make necessary changes like removing DataTable completely.
using NPOI.SS.UserModel; using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Text.RegularExpressions; namespace GenericExcelExport.ExcelExport { public class AbstractDataExportBridge : AbstractDataExport { public AbstractDataExportBridge() { _headers = new List<string>(); _type = new List<string>(); } public override void WriteData<T>(List<T> exportData) { PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T)); DataTable table = new DataTable(); foreach (PropertyDescriptor prop in properties) { var type = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType; _type.Add(type.Name); table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType); string name = Regex.Replace(prop.Name, "([A-Z])", " $1").Trim(); _headers.Add(name); } foreach (T item in exportData) { DataRow row = table.NewRow(); foreach (PropertyDescriptor prop in properties) row[prop.Name] = prop.GetValue(item) ?? DBNull.Value; table.Rows.Add(row); } IRow sheetRow = null; for (int i = 0; i < table.Rows.Count; i++) { sheetRow = _sheet.CreateRow(i + 1); for (int j = 0; j < table.Columns.Count; j++) { ICell Row1 = sheetRow.CreateCell(j); string cellvalue = Convert.ToString(table.Rows[i][j]); if (string.IsNullOrWhiteSpace(cellvalue)) { Row1.SetCellValue(string.Empty); } else if (_type[j].ToLower() == "string") { Row1.SetCellValue(cellvalue); } else if (_type[j].ToLower() == "int32") { Row1.SetCellValue(Convert.ToInt32(table.Rows[i][j])); } else if (_type[j].ToLower() == "double") { Row1.SetCellValue(Convert.ToDouble(table.Rows[i][j])); } else if (_type[j].ToLower() == "datetime") { Row1.SetCellValue(Convert.ToDateTime (table.Rows[i][j]).ToString("dd/MM/yyyy hh:mm:ss")); } else { Row1.SetCellValue(string.Empty); } } } } } }
Points of Interest
I came across this solution when I had over 20 forms to provide Excel export feature, and I wasn’t willing to use a traditional approach which will be lengthy in my case.
There are always areas of improvement in all things. If you see any areas of improvement here, please update in the comments.
History
- 27th April, 2018: Initial draft
- 12th August, 2019: Performance improvement based on new findings in my applications
This member has not yet provided a Biography. Assume it’s interesting and varied, and probably something to do with programming.