最近在维护公司的十年老项目。写了一些nopi的方法,记录一下。
/// <summary>
/// DataTable转成Excel表格
/// </summary>
/// <returns></returns>
public static byte[] DataTableToExcel(DataTable dt)
{
using (HSSFWorkbook hssfworkbook = new HSSFWorkbook())//建立Excel 2003对象
using (HSSFSheet sheet = (HSSFSheet)hssfworkbook.CreateSheet("sheet1"))//新建一个名称为sheet1的工作簿
using (MemoryStream stream = new MemoryStream()) //二进制流中介
{
DocumentSummaryInformation dsi = PropertySetFactory.CreateDocumentSummaryInformation();//配置文件属性
dsi.Company = string.Empty;
dsi.Category = string.Empty;//类别
dsi.Manager = string.Empty;//管理者
hssfworkbook.DocumentSummaryInformation = dsi;
SummaryInformation si = PropertySetFactory.CreateSummaryInformation();
si.Subject = string.Empty;//主题
si.Title = string.Empty;//标题
si.ApplicationName = string.Empty;//不知道是啥
si.Author = string.Empty;//作者
si.LastAuthor = string.Empty;//上一次保存者
si.Comments = string.Empty;//备注
si.CreateDateTime = DateTime.Now;
hssfworkbook.SummaryInformation = si;
//Func<HSSFWorkbook, string, short, HSSFCellStyle> setstyle =
// (hssfwb, fontname, fontsize)
// =>
// {
// HSSFFont font = (HSSFFont)hssfwb.CreateFont();
// HSSFCellStyle style = (HSSFCellStyle)hssfwb.CreateCellStyle();
// style.SetFont(font);
// font.FontName = fontname;
// font.FontHeightInPoints = fontsize;
// return style;
// };
////表格设置
//sheet.SetColumnWidth(1, 30 * 256);//第二列较宽
//sheet.DefaultColumnWidth = 13;
//HSSFCellStyle style0 = setstyle(hssfworkbook, "微软雅黑", 14);
//HSSFCellStyle style1 = setstyle(hssfworkbook, "微软雅黑", 12);
//设置列名
HSSFRow row = (HSSFRow)sheet.CreateRow(0);
for (int i = 0; i < dt.Columns.Count; i++)
{
Cell cell = row.CreateCell(i);
cell.SetCellValue(dt.Columns[i].ColumnName);
//cell.CellStyle = style0;
}
//单元格赋值
for (int i = 0; i < dt.Rows.Count; i++)
{
Row row1 = sheet.CreateRow(i + 1);
for (int j = 0; j < dt.Columns.Count; j++)
{
Cell cell = row1.CreateCell(j);
var value = dt.Rows[i][j];
if (value != null && value != DBNull.Value)
{
var valueType = value.GetType();
if (valueType == typeof(double) || valueType == typeof(decimal) || valueType == typeof(float) ||
valueType == typeof(double?) || valueType == typeof(decimal?) || valueType == typeof(float?) ||
valueType == typeof(byte) || valueType == typeof(int) || valueType == typeof(long) ||
valueType == typeof(byte?) || valueType == typeof(int?) || valueType == typeof(long?)
)
{
cell.SetCellValue(Convert.ToDouble(value));
}
else
{
cell.SetCellValue(value.ToString());
}
}
else
{
cell.SetCellValue(string.Empty);
}
//cell.CellStyle = style1;
}
}
hssfworkbook.Write(stream);
return stream.ToArray();
}
}
//使用
DataTable dt = GetDataTable();
dt.TableName = "导出的Excel";
DataTableToExcel(dt);
/// <summary>
/// 获取带数据的模版表格
/// </summary>
/// <param name="columns">Key为数据库的列名,Value为表格中显示的列名称。</param>
/// <param name="templetePath">模版文件的路径</param>
/// <param name="data">数据库中的数据</param>
/// <returns></returns>
public static byte[] GetDataTempleteExcel(Dictionary<string, string> columns, string templetePath, DataTable data)
{
using (FileStream fs = new FileStream(templetePath, FileMode.OpenOrCreate, FileAccess.ReadWrite))
using (HSSFWorkbook workbook = new HSSFWorkbook(fs))
using (HSSFSheet sheet1 = workbook.GetSheetAt(0) as HSSFSheet)
using (MemoryStream stream = new MemoryStream())
{
HSSFRow headerRow = sheet1.GetRow(0) as HSSFRow;//获取sheet的首行
int cellCount = headerRow.LastCellNum; //一行最后一个方格的编号 即总的列数
int cellRowsCount = sheet1.LastRowNum + 1;//这个好像是获取的下标,所以加一。
if (cellCount != columns.Count)
{
throw new ArgumentException(nameof(columns));
}
if (cellRowsCount < 1)
{
throw new ArgumentNullException(nameof(cellRowsCount));
}
//赋值前清除下数据
for (int i = 1; i < cellRowsCount; i++) if (sheet1.GetRow(i) == null) continue; else sheet1.RemoveRow(sheet1.GetRow(i));
DocumentSummaryInformation dsi = PropertySetFactory.CreateDocumentSummaryInformation();//配置文件属性
dsi.Company = string.Empty;
dsi.Category = string.Empty;//类别
dsi.Manager = string.Empty;//管理者
workbook.DocumentSummaryInformation = dsi;
SummaryInformation si = PropertySetFactory.CreateSummaryInformation();
si.Subject = string.Empty;//主题
si.Title = string.Empty;//标题
si.ApplicationName = string.Empty;//不知道是啥
si.Author = string.Empty;//作者
si.LastAuthor = string.Empty;//上一次保存者
si.Comments = string.Empty;//备注
si.CreateDateTime = DateTime.Now;
workbook.SummaryInformation = si;
DataRow[] dataRows = data.AsEnumerable().ToArray();
string[] columnNames = columns.Keys.ToArray();
//数据库中的列名,按照Excel中的列顺序排列。
List<string> dbColumnNames = new List<string>();
var columnRow = sheet1.GetRow(0);
for (int i = 0; i < cellCount; i++)
{
var excelColumnName = columnRow.GetCell(i).ToString().Trim();
foreach (var colKey in columnNames)
{
if (string.Equals(columns[colKey], excelColumnName, StringComparison.OrdinalIgnoreCase))
{
dbColumnNames.Add(colKey);
break;
}
}
}
//转成数组使用,因为感觉这样后面使用快一点。
string[] dbColumnNamesArray = dbColumnNames.ToArray();
if (dbColumnNamesArray.Length != cellCount)
{
throw new ArgumentException(nameof(dbColumnNamesArray));
}
//单元格赋值
for (int i = 0, c = dataRows.Length; i < c; i++)
{
Row row1 = sheet1.CreateRow(i + 1);
for (int j = 0; j < cellCount; j++)
{
Cell cell = row1.CreateCell(j);
var value = dataRows[i][dbColumnNamesArray[j]];
if (value != null && value != DBNull.Value)
{
cell.SetCellValue(value.ToString());
}
else
{
cell.SetCellValue(string.Empty);
}
}
}
workbook.Write(stream);
return stream.ToArray();
}
}
//使用
var columns = new Dictionary<string, string>()
{
{ "Name","名称" },
{ "Age","年龄" },
};
DataTable dt = GetDataTable();
dt.TableName = "导出的Excel";
DataTableToExcel(columns,Server.MapPath("~/template/test.xls"),dt);
/// <summary>
/// 获取excel单元格中的值
/// </summary>
/// <param name="cellObj"></param>
/// <returns></returns>
public static string GetExcelCellValue(Cell cellObj, NPOI.SS.UserModel.Workbook workbook)
{
if (cellObj == null) return string.Empty;
string cellValue = null;
switch (cellObj.CellType)
{
case CellType.Unknown:
break;
case CellType.NUMERIC:
//NPOI中数字和日期都是NUMERIC类型的,这里对其进行判断是否是日期类型
if (DateUtil.IsCellDateFormatted(cellObj))//日期类型
{
var date = cellObj.DateCellValue;
if (date != default(DateTime) && date != DateTime.MinValue)
{
cellValue = date.ToString("yyyy/MM/dd HH:mm:ss");
}
}
else//其他数字类型
{
cellValue = cellObj.NumericCellValue.ToString();
}
break;
case CellType.STRING:
cellValue = cellObj.StringCellValue;
break;
case CellType.FORMULA:
var eva = new NPOI.HSSF.UserModel.HSSFFormulaEvaluator(workbook);
cellValue = eva.Evaluate(cellObj).StringValue;
break;
case CellType.BLANK:
break;
case CellType.BOOLEAN:
cellValue = cellObj.BooleanCellValue.ToString();
break;
case CellType.ERROR:
cellValue = cellObj.ErrorCellValue.ToString();
break;
default:
break;
}
cellValue = string.IsNullOrWhiteSpace(cellValue) ? string.Empty : cellValue.Trim();
return cellValue;
}
/*下面的代码虽然是我写的,但是我不是很清楚啥作用了。而且是和业务以及asp的控件交织在一起的,所以这个也就记录下,不需要看懂。*/
/// <summary>
/// 上传Excel并且返回Excel中的数据
/// </summary>
/// <param name="columns">表列名和Excel中的列名字典</param>
/// <param name="fuInvoiceFile">上传文件的控件</param>
/// <returns>1:是否上传成功;2:上传成功后的数据;3:上传失败的错误消息;4:当前Excel的对象;5.文件上传后的地址</returns>
protected Tuple<bool, DataTable, string, Workbook, string> GetExcelDataTable(Dictionary<string, string> columns, FileUpload fuInvoiceFile)
{
#region 文件校验、上传到服务器
string fileName = fuInvoiceFile.ShortFileName.Replace(":", "_").Replace(" ", "_").Replace("\\", "_").Replace("/", "_");
if (fileName.LastIndexOf('.') <= 0)
{
return new Tuple<bool, DataTable, string, Workbook, string>(false, null, "- 文件错误!", null, null);
}
string fileExt = fileName.Substring(fileName.LastIndexOf("."));
// 文件验证
if (fileExt != ".xls" && fileExt != ".xlsx")
{
return new Tuple<bool, DataTable, string, Workbook, string>(false, null, "- 只能选择Excel文件导入!", null, null);
}
string onlyFileName = fileName.Substring(0, fileName.LastIndexOf(".") + 1);
string dir = $@"{Server.MapPath(@"\upfile\")}{DateTime.Now:yyyy-MM-dd}\";
if (!Directory.Exists(dir)) Directory.CreateDirectory(dir); // 创建日期目录
string filePath = $"{dir}{onlyFileName}{Guid.NewGuid().ToString().Replace("-", string.Empty)}{fileExt}"; // 获得文件保存在服务器上的路径
// 保存文件到服务器
fuInvoiceFile.PostedFile.SaveAs(filePath);
#endregion
#region 实例化一个Excel
FileStream fs = new FileStream(filePath, FileMode.Open);
NPOI.SS.UserModel.Workbook workbook = new NPOI.HSSF.UserModel.HSSFWorkbook(fs);
fs.Dispose();
NPOI.SS.UserModel.Sheet sheet = workbook.GetSheetAt(0);
if (sheet == null)
{
sheet = workbook.CreateSheet("Sheet1");
}
#endregion
#region 文件校验
if (sheet.PhysicalNumberOfRows < 2)
{
return new Tuple<bool, DataTable, string, Workbook, string>(false, null, "- 未获取到数据,请检查数据文件!", null, null);
}
var headerRow = sheet.GetRow(0) as NPOI.HSSF.UserModel.HSSFRow;//获取sheet的首行
int cellCount = headerRow.LastCellNum; //一行最后一个方格的编号 即总的列数
//int cellRowsCount = sheet.LastRowNum + 1;//这个好像是获取的下标,所以加一。
if (cellCount != columns.Count)
{
return new Tuple<bool, DataTable, string, Workbook, string>(false, null, "- 列数不正确,请检查Excel文件!", null, null);
}
DataTable dt = new DataTable();
var columnsKeys = columns.Keys.ToArray();
for (int j = 0, clos = columnsKeys.Length; j < clos; j++)
{
var headerText = headerRow.GetCell(j).ToString();
bool isNotExits = true;
foreach (var headerKey in columnsKeys)
{
var columnName = columns[headerKey];
if (columnName == headerText)
{
dt.Columns.Add(headerKey, typeof(string));
isNotExits = false;
break;
}
}
if (isNotExits)
{
return new Tuple<bool, DataTable, string, Workbook, string>(false, null, $"- 列名称[{headerText}]无法匹配,请使用系统提供的Excel模版导入!", null, null);
}
}
#endregion
#region 读取数据
for (int i = 1, rows = sheet.PhysicalNumberOfRows; i < rows; i++)
{
var dtRow = dt.NewRow();
for (int j = 0, clos = columnsKeys.Length; j < clos; j++)
{
Row excelRow = sheet.GetRow(i);
if (excelRow == null) excelRow = sheet.CreateRow(i);
Cell cellObj = excelRow.GetCell(j);
if (cellObj == null) cellObj = excelRow.CreateCell(j);
dtRow[j] = DataTableRenderToExcel.GetExcelCellValue(cellObj, workbook);
}
dt.Rows.Add(dtRow);
}
#endregion
#region 数据处理
/// <summary>
/// 删除DataTable下方的空数据行
/// </summary>
/// <param name="dt">要删除下方空数据行的数据列表</param>
/// <param name="startIndex">有效列开始下标</param>
/// <param name="endIndex">有效列结束下标</param>
void DeleteDataTableBelowNullData(DataTable dtd, int startIndex, int endIndex)
{
if (dtd == null) return;
int colLength = endIndex - startIndex + 1;
for (int i = dtd.Rows.Count - 1; i >= 0; i--)
{
var row = dtd.Rows[i].ItemArray;
int j = 0;
for (int y = startIndex; y <= endIndex; y++)
{
if (string.IsNullOrWhiteSpace(row?[y]?.ToString()))
{
j++;
}
else
{
break;
}
}
if (j == colLength)
{
dtd.Rows.RemoveAt(i);
}
else
{
break;
}
}
}
DeleteDataTableBelowNullData(dt, 1, dt.Columns.Count - 1);
#endregion
return new Tuple<bool, DataTable, string, Workbook, string>(true, dt, null, workbook, filePath);
}
/// <summary>
/// 执行Excel导入错误校验
/// </summary>
/// <param name="workbook">需要写入的Excel对象</param>
/// <param name="messages">错误消息集合</param>
/// <param name="filePath">Excel文件地址</param>
/// <returns></returns>
protected Tuple<bool, string> ExeExcelErrorMessage(Workbook workbook, List<string> messages, string filePath)
{
var sheet = workbook.GetSheetAt(0);
Row excelRowHead = sheet.GetRow(0);
int errorColIndex = excelRowHead.LastCellNum + 1;
if (excelRowHead == null) excelRowHead = sheet.CreateRow(0);
Cell excelCellErrorTipHead = excelRowHead.GetCell(errorColIndex);
if (excelCellErrorTipHead == null) excelCellErrorTipHead = excelRowHead.CreateCell(errorColIndex);
excelCellErrorTipHead.SetCellValue("导入提示");
bool hasError = false;
for (int i = 0, count = messages.Count; i < count; i++)
{
string errorMessage = messages[i];
Row excelRow = sheet.GetRow(i + 1);
if (errorMessage.Length > 0)
{
Cell cell = excelRow.GetCell(errorColIndex);
if (cell == null) cell = excelRow.CreateCell(errorColIndex);
cell.SetCellValue(errorMessage);
hasError = true;
}
}
string navigateUrl = null;
if (hasError)
{
navigateUrl = string.Format("~/upfile/{0}/{1}", DateTime.Now.ToString("yyyy-MM-dd"), filePath.Substring(filePath.LastIndexOf('\\') + 1));
var fs = new FileStream(Server.MapPath(navigateUrl), FileMode.OpenOrCreate, FileAccess.ReadWrite);
workbook.Write(fs);
fs.Dispose();
}
else
{
if (File.Exists(filePath)) File.Delete(filePath);
}
workbook.Dispose();
return new Tuple<bool, string>(hasError, navigateUrl);
}