企業應用中經常會遇到批量上傳數據的需求,客戶要求使用excel表格錄入一批數據,然後通過網頁上傳該excel表格,實現批量導入數據。這樣的需求其實主要就是處理Excel表格,把Excel表格中的內容讀取出來後,做一些必要的校驗,然後上傳校驗成功的數據,返回失敗數據的行號以及校驗失敗的信息。
如何讀取Excel表格,網上已經有很多方法,有些是通過oledb,有些是通過第三方組件。我參考了網上的一些代碼,加上自己的封裝,給大家提供一個改良後的版本。當然,為了做到通用,我沒有使用oledb的方式,而是使用了一個第三方組件。使用第三方組件的原因很簡單,因為有些服務器不一定安裝Excel,有些連oledb提供程序都沒有,所以哦,還是用第三方組件比較能夠通吃。
這個第三方組件叫做koogra,.NET實現的開源組件,比較好用。這裡是koogra項目地址:http://sourceforge.net/projects/koogra/
好了,開始我們的封裝過程,為了好用我希望的方式是四種,返回的結果都是DataTable對象。
1:傳入的參數包括工作簿地址、工作表名稱;
2:傳入的參數包括工作簿地址、工作表的索引(0表示第一個工作表);
3:傳入的參數包括內存流、工作表名稱;
4:傳入的參數包括內存流、工作表的索引(0表示第一個工作表)。
ok,開始上代碼,記得項目要引用Ionic.Utils.Zip.dll和Net.SourceForge.Koogra.dll
代碼
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using Net.SourceForge.Koogra.Excel;
namespace Gren.Framework.Office
{
/// <summary>
/// Excel工具類
/// </summary>
public class ExcelUtils
{
private Workbook book;
public ExcelUtils(string path)
{
this.book = new Workbook(path);
}
public ExcelUtils(System.IO.Stream stream)
{
this.book = new Workbook(stream);
}
protected DataTable SaveAsDataTable(Worksheet sheet)
{
DataTable dt = new DataTable();
uint minRow = sheet.Rows.MinRow;
uint maxRow = sheet.Rows.MaxRow;
Row firstRow = sheet.Rows[minRow];
uint minCol = firstRow.Cells.MinCol;
uint maxCol = firstRow.Cells.MaxCol;
for (uint i = minCol; i <= maxCol; i++)
{
dt.Columns.Add(firstRow.Cells[i].FormattedValue());
}
for (uint i = minRow + 1; i <= maxRow; i++)
{
Row row = sheet.Rows[i];
if (row != null)
{
DataRow dr = dt.NewRow();
for (uint j = minCol; j <= maxCol; j++)
{
Cell cell = row.Cells[j];
if (cell != null)
{
dr[Convert.ToInt32(j)] = cell.Value != null ? cell.Value.ToString() : string.Empty;
}
}
dt.Rows.Add(dr);
}
}
return dt;
}
public DataTable ToDataTable(int index)
{
Worksheet sheet = this.book.Sheets[index];
if (sheet == null)
{
throw new ApplicationException(string.Format("索引[{0}]所指定的電子表格不存在!", index));
}
return this.SaveAsDataTable(sheet);
}
public DataTable ToDataTable(string sheetName)
{
Worksheet sheet = this.book.Sheets.GetByName(sheetName);
if (sheet == null)
{
throw new ApplicationException(string.Format("名稱[{0}]所指定的電子表格不存在!", sheetName));
}
return this.SaveAsDataTable(sheet);
}
#region 靜態方法
/// <summary>
/// 單元格格式為日期時間,使用此方法轉換為DateTime類型,若解析失敗則返回‘0001-01-01’
/// </summary>
public static DateTime ParseDateTime(string cellValue)
{
DateTime date = default(DateTime);
double value = default(double);
if (double.TryParse(cellValue, out value))
{
date = DateTime.FromOADate(value);
}
else
{
DateTime.TryParse(cellValue, out date);
}
return date;
}
/// <summary>
/// 轉換為DataTable(文件路徑+表名)
/// </summary>
public static DataTable TranslateToTable(string path, string sheetName)
{
ExcelUtils utils = new ExcelUtils(path);
return utils.ToDataTable(sheetName);
}
/// <summary>
/// 轉換為DataTable(文件路徑+表索引)
/// </summary>
public static DataTable TranslateToTable(string path, int sheetIndex)
{
ExcelUtils utils = new ExcelUtils(path);
return utils.ToDataTable(sheetIndex);
}
/// <summary>
/// 轉換為DataTable(文件路徑)
/// </summary>
public static DataTable TranslateToTable(string path)
{
ExcelUtils utils = new ExcelUtils(path);
return utils.ToDataTable(0);
}
/// <summary>
/// 轉換為DataTable(內存流+表名)
/// </summary>
public static DataTable TranslateToTable(System.IO.Stream stream, string sheetName)
{
ExcelUtils utils = new ExcelUtils(stream);
return utils.ToDataTable(sheetName);
}
/// <summary>
/// 轉換為DataTable(內存流+表索引)
/// </summary>
public static DataTable TranslateToTable(System.IO.Stream stream, int sheetIndex)
{
ExcelUtils utils = new ExcelUtils(stream);
return utils.ToDataTable(sheetIndex);
}
/// <summary>
/// 轉換為DataTable(內存流)
/// </summary>
public static DataTable TranslateToTable(System.IO.Stream stream)
{
ExcelUtils utils = new ExcelUtils(stream);
return utils.ToDataTable(0);
}
#endregion
}
}
ParseDateTime這個靜態方法是用來處理日期時間類型單元格的,在Excel表格裡面日期時間類型讀取出來的時候是一個double類型的數據,使用這個方法可以得到正確的DataTime類型的值。仔細的人可以發現這個方法其實除了把double類型的數據轉換為DateTime之外,還會在轉換失敗的時候嘗試把該單元格的內容做為字符串轉換為DateTime,因為如果單元格是文本類型的話,那麼用戶輸入的日期時間就不是一個double了。
好了,讀取Excel的類就封裝好了,那麼在asp.net頁面中如何使用呢?再看下面的代碼,fileUpload1是文件上傳控件。
代碼
if (fileUpload1.HasFile)
{
DataTable dt = null;
try
{
using (System.IO.MemoryStream stream = new System.IO.MemoryStream(fileUpload1.FileBytes))
{
dt = Gren.Framework.Office.ExcelUtils.TranslateToTable(stream, "sheet1");
}
//得到DataTable對象後,做你自己的處理
}
catch (Exception ex)
{
lblMessage.Text = "<p><span class=\"c_red ft_bold\">處理數據文件錯誤:</span></p>";
lblMessage.Text += "<div class=\"c_red\">" + Server.HtmlEncode(ex.Message) + "</div>";
}
}
else
{
lblMessage.Text = "<p><span class=\"c_red ft_bold\">請選擇數據文件</span></p>";
}
直接用內存流來處理上傳的文件還是比較快的,大家試試吧,有什麼問題記得給我留言。或者大家幫我優化增強後,也請告訴我,共同進步嘛。