Excel导入及导出问题产生:



  从接触.net到现在一直在维护一个DataTable导出到Excel的类,时不时还会维护一个导入类。以下是时不时就会出现的问题:



导出问题:



  如果是asp.net,你得在服务器端装Office,几百M呢,还得及时更新它,以防漏洞,还得设定权限允许ASP.net访问COM+,听说如果导出过程中出问题可能导致服务器宕机。



  Excel会把只包含数字的列进行类型转换,本来是文本型的,它非要把你转成数值型的,像身份证后三位变成000,编号000123会变成123,够智能吧,够郁闷吧。不过这些都还是可以变通解决的,在他们前边加上一个字母,让他们不只包含数字。



  导出时,如果你的字段内容以"-"或"="开头,Excel好像把它当成了公式什么的,接下来就出错,提示:类似,保存到Sheet1的问题



导入问题:



  Excel会根据你的 Excel文件前8行分析数据类型,如果正好你前8行某一列只是数字,那它会认为你这一列就是数值型的,然后,身份证,手机,编号都转吧变成类似这样的1.42702E+17格式,日期列变成 包含日期和数字的,乱的很,可以通过改注册表让Excel分析整个表,但如果整列都是数字,那这个问题还是解决不了。




  以上问题,一般人初次做时肯定得上网查查吧,一个问题接着另一个问题,查到你郁郁而死,还有很多问题没解决,最终感觉已经解决的不错了,但还不能保证某一天还会出个什么问题。



使用第三方开源组件导入及导出Excel的解决方案:



  偶然间发现了NPOI与MyXls,相见恨晚,害的我在Excel上浪费了那么多时间,他们俩的好处是:就是.net的自定义类库,可以直接对Excel进行读或写,而不依赖Office 的 Excel,这不管对于ASP.net或Winform都非常有利,不用担心Excel进程的释放问题,服务器安全,设置,导出,导入“Excel智能识别”,公式日期等问题,可以说以前的Excel问题,全都不用管了,它们可以很好的帮你解决,NPOI || MyXls == 研究几年Excel。



  NPOI开源地址: ​​http://npoi.codeplex.com/​

  MyXls开源地址: ​​http://sourceforge.net/projects/myxls/​



下面来两个简单入门例子:


MyXls 快速入门例子:



1 /// <summary>
2 /// MyXls简单Demo,快速入门代码
3 /// </summary>
4 /// <paramname="dtSource"></param>
5 /// <paramname="strFileName"></param>
6 /// <remarks> MyXls认为Excel的第一个单元格是:(1,1) </remarks>
7 /// <Author> 柳永法 http://www.yongfa365.com/ 2010-5-822:21:41 </Author>
8 public static void ExportEasy(DataTabledtSource, string strFileName)
9 {
10 XlsDocumentxls = new XlsDocument();
11 Worksheetsheet = xls.Workbook.Worksheets.Add( " Sheet1 " );
12
13 // 填充表头
14 foreach (DataColumncol in dtSource.Columns)
15 {
16 sheet.Cells.Add( 1 ,col.Ordinal + 1 ,col.ColumnName);
17 }
18
19 // 填充内容
20 for ( int i = 0 ;i < dtSource.Rows.Count;i ++ )
21 {
22 for ( int j = 0 ;j < dtSource.Columns.Count;j ++ )
23 {
24 sheet.Cells.Add(i + 2 ,j + 1 ,dtSource.Rows[i][j].ToString());
25 }
26 }
27
28 // 保存
29 xls.FileName = strFileName;
30 xls.Save();
31 }



NPOI 快速入门例子:



1 /// <summary>
2 /// NPOI简单Demo,快速入门代码
3 /// </summary>
4 /// <paramname="dtSource"></param>
5 /// <paramname="strFileName"></param>
6 /// <remarks> NPOI认为Excel的第一个单元格是:(0,0) </remarks>
7 /// <Author> 柳永法 http://www.yongfa365.com/ 2010-5-822:21:41 </Author>
8 public static void ExportEasy(DataTabledtSource, string strFileName)
9 {
10 HSSFWorkbookworkbook = new HSSFWorkbook();
11 HSSFSheetsheet = workbook.CreateSheet();
12
13 // 填充表头
14 HSSFRowdataRow = sheet.CreateRow( 0 );
15 foreach (DataColumncolumn in dtSource.Columns)
16 {
17 dataRow.CreateCell(column.Ordinal).SetCellValue(column.ColumnName);
18 }
19
20
21 // 填充内容
22 for ( int i = 0 ;i < dtSource.Rows.Count;i ++ )
23 {
24 dataRow = sheet.CreateRow(i + 1 );
25 for ( int j = 0 ;j < dtSource.Columns.Count;j ++ )
26 {
27 dataRow.CreateCell(j).SetCellValue(dtSource.Rows[i][j].ToString());
28 }
29 }
30
31
32 // 保存
33 using (MemoryStreamms = new MemoryStream())
34 {
35 using (FileStreamfs = new FileStream(strFileName,FileMode.Create,FileAccess.Write))
36 {
37 workbook.Write(fs);
38 }
39 }
40 workbook.Dispose();
41 }




接下来是 ​​柳永法(yongfa365)'Blog​​封装的可以用在实际项目中的类,实现的功能有(仅NPOI):



  1. 支持web及winform从DataTable导出到Excel。
  2. 生成速度很快。
  3. 准确判断数据类型,不会出现身份证转数值等上面提到的一系列问题。
  4. 如果单页条数大于65535时会新建工作表。
  5. 列宽自适应。
  6. 支持读取Excel。
  7. 调用方便,只一调用一个静态类就OK了。
  8. 因为测试期间发现MyXls导出速度要比NPOI慢3倍,而NPOI既能满足我们的导出需求,又能很好的满足我们的导入需求,所以只针对NPOI进行全方位功能实现及优化。



MyXls导出相关类:



1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using org.in2bits.MyXls;
6 using org.in2bits.MyXls.ByteUtil;
7 using System.Data;
8
9 class ExcelHelper
10 {
11 public static void Export(DataTabledtSource, string strHeaderText, string strFileName)
12 {
13 XlsDocumentxls = new XlsDocument();
14 xls.FileName = DateTime.Now.ToString( " yyyyMMddHHmmssffff " ,System.Globalization.DateTimeFormatInfo.InvariantInfo);
15 xls.SummaryInformation.Author = " yongfa365 " ; // 填加xls文件作者信息
16 xls.SummaryInformation.NameOfCreatingApplication = " liuyongfa " ; // 填加xls文件创建程序信息
17 xls.SummaryInformation.LastSavedBy = " LastSavedBy " ; // 填加xls文件最后保存者信息
18 xls.SummaryInformation.Comments = " Comments " ; // 填加xls文件作者信息
19 xls.SummaryInformation.Title = " title " ; // 填加xls文件标题信息
20 xls.SummaryInformation.Subject = " Subject " ; // 填加文件主题信息
21 xls.DocumentSummaryInformation.Company = " company " ; // 填加文件公司信息
22
23
24 Worksheetsheet = xls.Workbook.Worksheets.Add( " Sheet1 " ); // 状态栏标题名称
25 Cellscells = sheet.Cells;
26
27 foreach (DataColumncol in dtSource.Columns)
28 {
29 Cellcell = cells.Add( 1 ,col.Ordinal + 1 ,col.ColumnName);
30 cell.Font.FontFamily = FontFamilies.Roman; // 字体
31 cell.Font.Bold = true ; // 字体为粗体
32
33 }
34 #region 填充内容
35 XFdateStyle = xls.NewXF();
36 dateStyle.Format = " yyyy-mm-dd " ;
37
38 for ( int i = 0 ;i < dtSource.Rows.Count;i ++ )
39 {
40 for ( int j = 0 ;j < dtSource.Columns.Count;j ++ )
41 {
42
43 int rowIndex = i + 2 ;
44 int colIndex = j + 1 ;
45 string drValue = dtSource.Rows[i][j].ToString();
46
47 switch (dtSource.Rows[i][j].GetType().ToString())
48 {
49 case " System.String " : // 字符串类型
50 cells.Add(rowIndex,colIndex,drValue);
51 break ;
52 case " System.DateTime " : // 日期类型
53 DateTimedateV;
54 DateTime.TryParse(drValue, out dateV);
55 cells.Add(rowIndex,colIndex,dateV,dateStyle);
56 break ;
57 case " System.Boolean " : // 布尔型
58 bool boolV = false ;
59 bool .TryParse(drValue, out boolV);
60 cells.Add(rowIndex,colIndex,boolV);
61 break ;
62 case " System.Int16 " : // 整型
63 case " System.Int32 " :
64 case " System.Int64 " :
65 case " System.Byte " :
66 int intV = 0 ;
67 int .TryParse(drValue, out intV);
68 cells.Add(rowIndex,colIndex,intV);
69 break ;
70 case " System.Decimal " : // 浮点型
71 case " System.Double " :
72 double doubV = 0 ;
73 double .TryParse(drValue, out doubV);
74 cells.Add(rowIndex,colIndex,doubV);
75 break ;
76 case " System.DBNull " : // 空值处理
77 cells.Add(rowIndex,colIndex, null );
78 break ;
79 default :
80 cells.Add(rowIndex,colIndex, null );
81 break ;
82 }
83 }
84 }
85
86 #endregion
87
88 xls.FileName = strFileName;
89 xls.Save();
90 }
91 }
92




NPOI导入导出相关类:



1 using System;
2 using System.Collections.Generic;
3 using System.Data;
4 using System.IO;
5 using System.Text;
6 using System.Web;
7 using NPOI;
8 using NPOI.HPSF;
9 using NPOI.HSSF;
10 using NPOI.HSSF.UserModel;
11 using NPOI.HSSF.Util;
12 using NPOI.POIFS;
13 using NPOI.Util;
14
15
16 public class ExcelHelper
17 {
18 /// <summary>
19 /// DataTable导出到Excel文件
20 /// </summary>
21 /// <paramname="dtSource"> 源DataTable </param>
22 /// <paramname="strHeaderText"> 表头文本 </param>
23 /// <paramname="strFileName"> 保存位置 </param>
24 /// <Author> 柳永法 http://www.yongfa365.com/ 2010-5-822:21:41 </Author>
25 public static void Export(DataTabledtSource, string strHeaderText, string strFileName)
26 {
27 using (MemoryStreamms = Export(dtSource,strHeaderText))
28 {
29 using (FileStreamfs = new FileStream(strFileName,FileMode.Create,FileAccess.Write))
30 {
31 byte []data = ms.ToArray();
32 fs.Write(data, 0 ,data.Length);
33 fs.Flush();
34 }
35 }
36 }
37
38 /// <summary>
39 /// DataTable导出到Excel的MemoryStream
40 /// </summary>
41 /// <paramname="dtSource"> 源DataTable </param>
42 /// <paramname="strHeaderText"> 表头文本 </param>
43 /// <Author> 柳永法 http://www.yongfa365.com/ 2010-5-822:21:41 </Author>
44 public static MemoryStreamExport(DataTabledtSource, string strHeaderText)
45 {
46 HSSFWorkbookworkbook = new HSSFWorkbook();
47 HSSFSheetsheet = workbook.CreateSheet();
48
49 #region 右击文件属性信息
50 {
51 DocumentSummaryInformationdsi = PropertySetFactory.CreateDocumentSummaryInformation();
52 dsi.Company = " http://www.yongfa365.com/ " ;
53 workbook.DocumentSummaryInformation = dsi;
54
55 SummaryInformationsi = PropertySetFactory.CreateSummaryInformation();
56 si.Author = " 柳永法 " ; // 填加xls文件作者信息
57 si.ApplicationName = " NPOI测试程序 " ; // 填加xls文件创建程序信息
58 si.LastAuthor = " 柳永法2 " ; // 填加xls文件最后保存者信息
59 si.Comments = " 说明信息 " ; // 填加xls文件作者信息
60 si.Title = " NPOI测试 " ; // 填加xls文件标题信息
61 si.Subject = " NPOI测试Demo " ; // 填加文件主题信息
62 si.CreateDateTime = DateTime.Now;
63 workbook.SummaryInformation = si;
64 }
65 #endregion
66
67 HSSFCellStyledateStyle = workbook.CreateCellStyle();
68 HSSFDataFormatformat = workbook.CreateDataFormat();
69 dateStyle.DataFormat = format.GetFormat( " yyyy-mm-dd " );
70
71 // 取得列宽
72 int []arrColWidth = new int [dtSource.Columns.Count];
73 foreach (DataColumnitem in dtSource.Columns)
74 {
75 arrColWidth[item.Ordinal] = Encoding.GetEncoding( 936 ).GetBytes(item.ColumnName.ToString()).Length;
76 }
77 for ( int i = 0 ;i < dtSource.Rows.Count;i ++ )
78 {
79 for ( int j = 0 ;j < dtSource.Columns.Count;j ++ )
80 {
81 int intTemp = Encoding.GetEncoding( 936 ).GetBytes(dtSource.Rows[i][j].ToString()).Length;
82 if (intTemp > arrColWidth[j])
83 {
84 arrColWidth[j] = intTemp;
85 }
86 }
87 }
88
89
90
91 int rowIndex = 0 ;
92
93 foreach (DataRowrow in dtSource.Rows)
94 {
95 #region 新建表,填充表头,填充列头,样式
96 if (rowIndex == 65535 || rowIndex == 0 )
97 {
98 if (rowIndex != 0 )
99 {
100 sheet = workbook.CreateSheet();
101 }
102
103 #region 表头及样式
104 {
105 HSSFRowheaderRow = sheet.CreateRow( 0 );
106 headerRow.HeightInPoints = 25 ;
107 headerRow.CreateCell( 0 ).SetCellValue(strHeaderText);
108
109 HSSFCellStyleheadStyle = workbook.CreateCellStyle();
110 headStyle.Alignment = CellHorizontalAlignment.CENTER;
111 HSSFFontfont = workbook.CreateFont();
112 font.FontHeightInPoints = 20 ;
113 font.Boldweight = 700 ;
114 headStyle.SetFont(font);
115
116 headerRow.GetCell( 0 ).CellStyle = headStyle;
117
118 sheet.AddMergedRegion( new Region( 0 , 0 , 0 ,dtSource.Columns.Count - 1 ));
119 headerRow.Dispose();
120 }
121 #endregion
122
123
124 #region 列头及样式
125 {
126 HSSFRowheaderRow = sheet.CreateRow( 1 );
127
128
129 HSSFCellStyleheadStyle = workbook.CreateCellStyle();
130 headStyle.Alignment = CellHorizontalAlignment.CENTER;
131 HSSFFontfont = workbook.CreateFont();
132 font.FontHeightInPoints = 10 ;
133 font.Boldweight = 700 ;
134 headStyle.SetFont(font);
135
136
137 foreach (DataColumncolumn in dtSource.Columns)
138 {
139 headerRow.CreateCell(column.Ordinal).SetCellValue(column.ColumnName);
140 headerRow.GetCell(column.Ordinal).CellStyle = headStyle;
141
142 // 设置列宽
143 sheet.SetColumnWidth(column.Ordinal,(arrColWidth[column.Ordinal] + 1 ) * 256 );
144
145 }
146 headerRow.Dispose();
147 }
148 #endregion
149
150 rowIndex = 2 ;
151 }
152 #endregion
153
154
155 #region 填充内容
156 HSSFRowdataRow = sheet.CreateRow(rowIndex);
157 foreach (DataColumncolumn in dtSource.Columns)
158 {
159 HSSFCellnewCell = dataRow.CreateCell(column.Ordinal);
160
161 string drValue = row[column].ToString();
162
163 switch (column.DataType.ToString())
164 {
165 case " System.String " : // 字符串类型
166 newCell.SetCellValue(drValue);
167 break ;
168 case " System.DateTime " : // 日期类型
169 DateTimedateV;
170 DateTime.TryParse(drValue, out dateV);
171 newCell.SetCellValue(dateV);
172
173 newCell.CellStyle = dateStyle; // 格式化显示
174 break ;
175 case " System.Boolean " : // 布尔型
176 bool boolV = false ;
177 bool .TryParse(drValue, out boolV);
178 newCell.SetCellValue(boolV);
179 break ;
180 case " System.Int16 " : // 整型
181 case " System.Int32 " :
182 case " System.Int64 " :
183 case " System.Byte " :
184 int intV = 0 ;
185 int .TryParse(drValue, out intV);
186 newCell.SetCellValue(intV);
187 break ;
188 case " System.Decimal " : // 浮点型
189 case " System.Double " :
190 double doubV = 0 ;
191 double .TryParse(drValue, out doubV);
192 newCell.SetCellValue(doubV);
193 break ;
194 case " System.DBNull " : // 空值处理
195 newCell.SetCellValue( "" );
196 break ;
197 default :
198 newCell.SetCellValue( "" );
199 break ;
200 }
201
202 }
203 #endregion
204
205 rowIndex ++ ;
206 }
207
208
209 using (MemoryStreamms = new MemoryStream())
210 {
211 workbook.Write(ms);
212 ms.Flush();
213 ms.Position = 0 ;
214
215 sheet.Dispose();
216 // workbook.Dispose(); // 一般只用写这一个就OK了,他会遍历并释放所有资源,但当前版本有问题所以只释放sheet
217 return ms;
218 }
219
220 }
221
222
223 /// <summary>
224 /// 用于Web导出
225 /// </summary>
226 /// <paramname="dtSource"> 源DataTable </param>
227 /// <paramname="strHeaderText"> 表头文本 </param>
228 /// <paramname="strFileName"> 文件名 </param>
229 /// <Author> 柳永法 http://www.yongfa365.com/ 2010-5-822:21:41 </Author>
230 public static void ExportByWeb(DataTabledtSource, string strHeaderText, string strFileName)
231 {
232
233 HttpContextcurContext = HttpContext.Current;
234
235 // 设置编码和附件格式
236 curContext.Response.ContentType = " application/vnd.ms-excel " ;
237 curContext.Response.ContentEncoding = Encoding.UTF8;
238 curContext.Response.Charset = "" ;
239 curContext.Response.AppendHeader( " Content-Disposition " ,
240 " attachment;filename= " + HttpUtility.UrlEncode(strFileName,Encoding.UTF8));
241
242 curContext.Response.BinaryWrite(Export(dtSource,strHeaderText).GetBuffer());
243 curContext.Response.End();
244
245 }
246
247
248 /// <summary> 读取excel
249 /// 默认第一行为标头
250 /// </summary>
251 /// <paramname="strFileName"> excel文档路径 </param>
252 /// <returns></returns>
253 public static DataTableImport( string strFileName)
254 {
255 DataTabledt = new DataTable();
256
257 HSSFWorkbookhssfworkbook;
258 using (FileStreamfile = new FileStream(strFileName,FileMode.Open,FileAccess.Read))
259 {
260 hssfworkbook = new HSSFWorkbook(file);
261 }
262 HSSFSheetsheet = hssfworkbook.GetSheetAt( 0 );
263 System.Collections.IEnumeratorrows = sheet.GetRowEnumerator();
264
265 HSSFRowheaderRow = sheet.GetRow( 0 );
266 int cellCount = headerRow.LastCellNum;
267
268 for ( int j = 0 ;j < cellCount;j ++ )
269 {
270 HSSFCellcell = headerRow.GetCell(j);
271 dt.Columns.Add(cell.ToString());
272 }
273
274 for ( int i = (sheet.FirstRowNum + 1 );i <= sheet.LastRowNum;i ++ )
275 {
276 HSSFRowrow = sheet.GetRow(i);
277 DataRowdataRow = dt.NewRow();
278
279 for ( int j = row.FirstCellNum;j < cellCount;j ++ )
280 {
281 if (row.GetCell(j) != null )
282 dataRow[j] = row.GetCell(j).ToString();
283 }
284
285 dt.Rows.Add(dataRow);
286 }
287 return dt;
288 }
289
290 }




以上相关源码及测试用例下载地址:



参考地址:


在 Server 端存取 Excel 檔案的利器:NPOI Library: ​​http://msdn.microsoft.com/zh-tw/ee818993.aspx​




总结:



  通过以上分析,我们不难发现,用NPOI或MyXls代替是Excel是很明智的,在发文前,我看到NPOI及MyXls仍然在活跃的更新中。在使用过程中发现这两个组件极相似,以前看过文章说他们使用的内核是一样的。还有NPOI是国人开发的,且有相关中文文档,在很多地方有相关引用,下载量也很大。并且它支持Excel,看到MyXls相关问题基本上没人回答,所以推荐使用NPOI。MyXls可以直接Cell.Font.Bold操作,而NPOI得使用CellType多少感觉有点麻烦。