.net使用NPOI的XSSFWorkbook進行web開發中導出Excel

以前也使用過NPOI導出excel,此次是由於在導出的excel裏新增了幾個列,正好超出了255的限制,因此又要改了。html

今天主要出了4個問題:c#

1. Invalid column index (256). Allowable column range for BIFF8 is (0..255) or ('A'..'IV') app

2.c# npoi  XSSFWorkbook沒法訪問已關閉的流。框架

3.因爲代碼已通過優化或者本機框架位於調用堆棧之上,沒法計算表達式的值。xss

4.錯誤提示: Excel在「春天Excel2007.xlsx」中發現不可讀取內容。是否恢復工做簿的內容?若是信任此工做簿的來源,請單擊「是」。 單擊「是」後:Excel 已完成文件級驗證和修復。此工做簿的某些部分可能已被修復或丟棄。ide

經歷了一上午,不斷百度,進行了不少次嘗試,也經歷了幾回項目的平衡,因此作一個總結。優化

以前的導出是這樣的,使用hssf方法,代碼以下:spa

1 public static MemoryStream RenderToExcel(DataTable table) 2 { 3 MemoryStream ms = new MemoryStream(); 4 using (table) 5  { 6 IWorkbook workbook = new HSSFWorkbook(); 7 ISheet sheet = workbook.CreateSheet(); 8 IRow headerRow = sheet.CreateRow(0); 9 foreach (DataColumn column in table.Columns) 10  { 11 var headCell = headerRow.CreateCell(column.Ordinal); 12  headCell.SetCellValue(column.Caption); 13  } 14 int rowIndex = 1; 15 foreach (DataRow row in table.Rows) 16  { 17 IRow dataRow = sheet.CreateRow(rowIndex); 18 foreach (DataColumn column in table.Columns) 19  { 20 var cellell = dataRow.CreateCell(column.Ordinal); 21  cellell.SetCellValue(row[column].ToString()); 22  } 23 rowIndex++; 24  } 25  workbook.Write(ms); 26  ms.Flush(); 27 ms.Position = 0; 28  } 29 return ms; 30 } 

這樣導出的是「.XLS」格式的excel表格,因此在列數超過256以後就會提示第一個提示錯誤。上網百度,找了不少,參考https://www.cnblogs.com/jRoger/p/4602239.html一篇文章,說是office2007以後,能夠建立的列數大多了,才才用了XSSF的方法。excel

剛開始直接將hssf換成xssf,結果提示第二個錯誤,沒法訪問已關閉的流。再百度,參考https://www.cnblogs.com/caoyc/p/6215210.html文章,說NPOI生產.xlsx文件件時,在使用book.Write(ms);後,會關閉流,這樣致使再次使用Respons輸出流的時候就出錯了。使用自定義流代替內存流,這樣就能夠保證流不會被自定關閉了。改寫代碼以下:code

1 public class NPOIMemoryStream : MemoryStream 2 { 3 /// <summary> 4 /// 獲取流是否關閉 5 /// </summary> 6 public bool IsColse 7  { 8 get; 9 private set; 10  } 11 public NPOIMemoryStream(bool colse = false) 12  { 13 IsColse = colse; 14  } 15 public override void Close() 16  { 17 if (IsColse) 18  { 19 base.Close(); 20  } 21  } 22 } 23 public static MemoryStream RenderToExcelNew(DataTable table) 24 { 25 NPOIMemoryStream ms = new NPOIMemoryStream(); 26 using (table) 27  { 28 IWorkbook workbook = new XSSFWorkbook(); 29 ISheet sheet = null; 30 sheet =workbook.CreateSheet(); 31 IRow headerRow = sheet.CreateRow(0); 32 foreach (DataColumn column in table.Columns) 33  { 34 var headCell = headerRow.CreateCell(column.Ordinal); 35  headCell.SetCellValue(column.Caption); 36  } 37 int rowIndex = 1; 38 foreach (DataRow row in table.Rows) 39  { 40 IRow dataRow = sheet.CreateRow(rowIndex); 41 foreach (DataColumn column in table.Columns) 42  { 43 var cellell = dataRow.CreateCell(column.Ordinal); 44  cellell.SetCellValue(row[column].ToString()); 45  } 46 rowIndex++; 47  } 48  workbook.Write(ms); 49  ms.Flush(); 50 ms.Position = 0; 51  } 52 return ms; 53 } 

注意上面是新建了一個NPOIMemoryStream類,它繼承於MemoryStream。下面的導出方法仍是和以前的相似。有興趣的讀者,能夠將它們合併成

1 if (fileName.IndexOf(".xlsx") > 0) // 2007版本
2  workbook = new XSSFWorkbook(fs); 3 else if (fileName.IndexOf(".xls") > 0) // 2003版本 4 workbook = new HSSFWorkbook(fs);

這樣的形式。
另外在這個過程當中,由於項目中引用NPOI的位置有兩個,致使兩個加載以後的版本不一致,報這樣的錯誤「未能加載文件或程序集「ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73」或它的某一個依賴項」。因而一不作二不休,將文件所有啓用NuGet程序包,來獲取NPOI。雖然它的速度特別慢。可是因爲以前在項目中新建了文件夾存放4個NPOI的dll,致使有一個項目的引用始終不是NuGet的程序包。當時還一度想放棄了,我把列數降下來不就行啦,費那老鼻子勁幹嗎。中午午休以後,想了想,本身仍是把這個問題解決吧。因而仔細看了dll引用路徑以後,將項目的npoi文件夾刪除了,果真引用就正確了。

個人導出是流的形式,生成以後導出的代碼是:

1 using (MemoryStream ms = RenderToExcelNew(table)) 2 { 3  RenderToBrowser(ms, context, fileName); 4 } 5 public static void RenderToBrowser(MemoryStream ms, HttpContext context, string fileName) 6 { 7  context.Response.Clear(); 8 context.Response.ContentEncoding = Encoding.GetEncoding("GB2312"); 9 context.Response.ContentType = "application/octet-stream"; 10 context.Response.AddHeader("Content-Disposition", "attachment;fileName=" + fileName); 11  context.Response.BinaryWrite(ms.ToArray()); 12  context.Response.Flush(); 13  context.Response.End(); 14 }

這樣就出現了第3個問題,「因爲代碼已通過優化或者本機框架位於調用堆棧之上,沒法計算表達式的值。」,異常報錯在最後一行,Response.End() 因爲代碼已通過優化或者本機框架位於調用堆棧之上,沒法計算表達式的值。再百度吧,看到https://www.cnblogs.com/Alben-wang/p/6096568.html,因而將Response.End,改爲調用HttpContext.Current.ApplicationInstance.CompleteRequest方法。因而最後一行爲context.ApplicationInstance.CompleteRequest();。

但仍是有問題,運行了一遍程序以後,就爆出了第4個錯誤。文件導出以後,提示:Excel在「xxxxxx.xlsx」中發現不可讀取內容。是否恢復工做簿的內容?若是信任此工做簿的來源,請單擊「是」。 單擊「是」後:Excel 已完成文件級驗證和修復。此工做簿的某些部分可能已被修復或丟棄。再百度吧,看到了https://www.cnblogs.com/qk2014/p/7729215.html,這篇文章,介紹的很好。加上設置大小下載下來的.xlsx文件打開時纔不會報「Excel 已完成文件級驗證和修復。此工做簿的某些部分可能已被修復或丟棄」

long fileSize = ms.Length;
context.Response.AddHeader("Content-Length", fileSize.ToString());
便可。

因而最後的代碼就變成了:

1 public static void RenderToBrowser(MemoryStream ms, HttpContext context, string fileName) 2 { 3  context.Response.Clear(); 4 context.Response.ContentEncoding = Encoding.GetEncoding("GB2312"); 5 context.Response.ContentType = "application/octet-stream"; 6 context.Response.AppendHeader("Content-Disposition", "attachment; filename=" + fileName); 7 long fileSize = ms.Length; 8 context.Response.AddHeader("Content-Length", fileSize.ToString()); 9  context.Response.BinaryWrite(ms.ToArray()); 10  context.Response.Flush(); 11  context.ApplicationInstance.CompleteRequest(); 12 }

注意兩段代碼的第6行,略微有點不一樣。

至此忙活了一上午和中午,就獲得了這樣的成就,仍是有不少不懂的地方啊。

看到網上有不少是導出成datatable形式的,也留幾個連接參考:https://www.cnblogs.com/lazyneal/p/6148912.html,

https://www.cnblogs.com/luxiaoxun/p/3374992.html,https://www.cnblogs.com/a14907/p/6423733.html

相關文章
相關標籤/搜索