之前寫過一篇使用使用NPOI完全脫離excel的導出二,批量數據導出性能優化,但在實際使用中發現,導出五萬條數據,需要近三十秒,這還不包括讀取數據的時間。於是看看還能不能優化。
通過測試發現速度慢主要體現在一下兩個方面:
1是把IWorkbook寫入內存流,看着似乎沒辦法修改。
internal static MemoryStream Export(IWorkbook workbook,int count)
{
try
{
MemoryStream ms = new MemoryStream(count*20);
workbook.Write(ms);
return ms;
}
catch (Exception ex)
{
new AppException("導出文件錯誤MemoryStream", ex);
return null;
}
}
2循環輸出Excel,在循環中使用了反射來提取數據,於是把反射修改如下:
public static Func<T, object> EmitGetter<T>(string propertyName)
{
var type = typeof(T);
var dynamicMethod = new DynamicMethod("get_" + propertyName, typeof(object), new[] { type }, type);
var iLGenerator = dynamicMethod.GetILGenerator();
iLGenerator.Emit(OpCodes.Ldarg_0);
var property = type.GetProperty(propertyName);
iLGenerator.Emit(OpCodes.Callvirt, property.GetGetMethod(true));
if (property.PropertyType.IsValueType)
{
// 如果是值類型,裝箱
iLGenerator.Emit(OpCodes.Box, property.PropertyType);
}
else
{
// 如果是引用類型,轉換
iLGenerator.Emit(OpCodes.Castclass, property.PropertyType);
}
iLGenerator.Emit(OpCodes.Ret);
return dynamicMethod.CreateDelegate(typeof(Func<T, object>)) as Func<T, object>;
}
public static object EmitGetterValue<T>(T model, string propertyName)
{
var getMethod = EmitGetter<T>(propertyName);
return getMethod(model);
}
3在設置Excel值是使用了typeof,於是也修改如下
在生成字典時,生成字段和類型關係的字典
Dictionary<string, FieldType> FieldNamesPropertyInfoDic = new Dictionary<string, FieldType>();
Dictionary<string, Func<Titem, object>> FieldNamesMethodInfoDic = new Dictionary<string, Func<Titem, object>>();
if (list.Count > 0)
{
foreach (KeyValuePair<string, string> column in FieldNames)
{
var getterMethod = EmitGetter<Titem>(column.Key);
string key = column.Key;
PropertyInfo pinfo = typeof(Titem).GetProperty(key);
if (pinfo != null)
{
if (!FieldNamesPropertyInfoDic.ContainsKey(key))
{
Type modePropertyType = pinfo.PropertyType;
FieldType fieldType= ExportTreeData<Titem>.GetFieldType(modePropertyType);
FieldNamesPropertyInfoDic.Add(key, fieldType);
FieldNamesMethodInfoDic.Add(key, getterMethod);
}
}
else
{
new AppException("導出文件錯誤未找到關聯的屬性" + column.Key);
throw new Exception("未找到關聯的屬性" + column.Key + "");
}
}
}
else
{
return sheet;
}
設置單元格的值
這樣修改之後性能確實有所提高,但只能提高百分之二十左右,沒有質的飛躍,於是有尋找其他的方法。
想不到竟然找到了。
竟然也是一句代碼,修改IWorkbook的類型
private IWorkbook Getworkbook()
{
if (workbook == null)
{
// workbook = new HSSFWorkbook();//// 2003版本
XSSFWorkbook workbooktmp = new XSSFWorkbook();//2007版本
workbook = new SXSSFWorkbook(workbooktmp,500);
}
return workbook;
}
XSSFWorkbook修改爲SXSSFWorkbook二十秒直接變成了六秒!!!
唯一的問題是這種類型沒有了,日期類型,只能把日期類型做爲字符串處理。
相關代碼:
枚舉定義:
/*
* 根據單元的SetCellValue的方法設置四種枚舉值
void SetCellValue(bool value);
void SetCellValue(string value);
void SetCellValue(IRichTextString value);
void SetCellValue(DateTime value);
void SetCellValue(double value);
*/
public enum FieldType
{
//字符
Sstring,
//時間
SDateTime,
//數值
Sdouble,
//bool值
Sbool
}
根據反射獲取枚舉
public static FieldType GetFieldType(Type modePropertyType)
{
if (modePropertyType == typeof(string))
{
return FieldType.Sstring;
}
else if (modePropertyType == typeof(DateTime))
{
return FieldType.SDateTime;
}
else if (modePropertyType == typeof(DateTime?))
{
return FieldType.SDateTime;
}
else if (modePropertyType == typeof(bool))
{
return FieldType.Sbool;
}
else if (modePropertyType == typeof(bool?))
{
return FieldType.Sbool;
}
else if (modePropertyType == typeof(float?))
{
return FieldType.Sdouble;
}
else if (modePropertyType == typeof(decimal?))
{
return FieldType.Sdouble;
}
else if (modePropertyType == typeof(double?))
{
return FieldType.Sdouble;
}
else if (modePropertyType == typeof(int?))
{
return FieldType.Sdouble;
}
else if (modePropertyType == typeof(int))
{
return FieldType.Sdouble; ;
}
else if (modePropertyType == typeof(float))
{
return FieldType.Sdouble;
}
else if (modePropertyType == typeof(double))
{
return FieldType.Sdouble;
}
else if (modePropertyType == typeof(decimal))
{
return FieldType.Sdouble;
}
else if (modePropertyType == typeof(sbyte))
{
return FieldType.Sdouble;
}
else if (modePropertyType == typeof(sbyte?))
{
return FieldType.Sdouble;
}
else if (modePropertyType == typeof(byte))
{
return FieldType.Sdouble;
}
else if (modePropertyType == typeof(byte?))
{
return FieldType.Sdouble;
}
else if (modePropertyType == typeof(short))
{
return FieldType.Sdouble;
}
else if (modePropertyType == typeof(short?))
{
return FieldType.Sdouble;
}
else if (modePropertyType == typeof(ushort))
{
return FieldType.Sdouble;
}
else if (modePropertyType == typeof(ushort?))
{
return FieldType.Sdouble;
}
else if (modePropertyType == typeof(uint))
{
return FieldType.Sdouble;
}
else if (modePropertyType == typeof(uint?))
{
return FieldType.Sdouble;
}
else if (modePropertyType == typeof(long))
{
return FieldType.Sdouble;
}
else if (modePropertyType == typeof(long?))
{
return FieldType.Sdouble;
}
else if (modePropertyType == typeof(ulong))
{
return FieldType.Sdouble;
}
else if (modePropertyType == typeof(ulong?))
{
return FieldType.Sdouble;
}
return FieldType.Sstring;
}
設置單元格的值
public static void SetCellValue(FieldType modePropertyType, ICell newCell, string drValue)
{
if (string.IsNullOrEmpty(drValue))
{
return;
}
switch(modePropertyType)
{
case FieldType.Sstring:
newCell.SetCellValue(drValue);
break;
case FieldType.SDateTime:
//SXSSFWorkbook沒有實現時間的方法
newCell.SetCellValue(Convert.ToDateTime(drValue).ToString("yyyy-MM-dd"));
break;
case FieldType.Sdouble:
newCell.SetCellValue(Convert.ToDouble(drValue));
break;
case FieldType.Sbool:
newCell.SetCellValue(Convert.ToBoolean(drValue));
break;
default:
newCell.SetCellValue(drValue);
break;
}
}