我正在使用NPOI将DataTable转换为ASP.NET Web API项目中的Excel.
但我从回应中得不到任何东西.这是我的代码:
public HttpResponseMessage GetExcelFromDataTable(DataTable dt) { IWorkbook workbook = new XSSFWorkbook(); // create *.xlsx file, use HSSFWorkbook() for creating *.xls file. ISheet sheet1 = workbook.CreateSheet(); IRow row1 = sheet1.CreateRow(0); for (int i = 0; dt.Columns.Count > i; i++) { row1.CreateCell(i).SetCellValue(dt.Columns[i].ColumnName); } for (int i = 0; dt.Rows.Count > i; i++) { IRow row = sheet1.CreateRow(i + 1); for (int j = 0; dt.Columns.Count > j; j++) { row.CreateCell(j).SetCellValue(dt.Rows[i][j].ToString()); } } MemoryStream ms = new MemoryStream(); workbook.Write(ms); HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK); result.Content = new StreamContent(ms); result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); result.Content.Headers.ContentDisposition.FileName = string.Format("{0}.xlsx", dt.TableName); return result; }
我设置了一个断点来检查ms.Length
after workbook.Write(ms)
,但它返回一个异常:System.ObjectDisposedException
.
我哪里做错了?
此问题的另一个解决方法是......不使用多个MemoryStream
对象.
创建一个NpoiMemoryStream
继承MemoryStream
并覆盖该Close
方法的类:
public class NpoiMemoryStream : MemoryStream { public NpoiMemoryStream() { // We always want to close streams by default to // force the developer to make the conscious decision // to disable it. Then, they're more apt to remember // to re-enable it. The last thing you want is to // enable memory leaks by default. ;-) AllowClose = true; } public bool AllowClose { get; set; } public override void Close() { if (AllowClose) base.Close(); } }
然后,像这样使用该流:
var ms = new NpoiMemoryStream(); ms.AllowClose = false; workbook.Write(ms); ms.Flush(); ms.Seek(0, SeekOrigin.Begin); ms.AllowClose = true;
在flush和seek之间的某个时刻,NPOI将尝试关闭流,但由于我们覆盖Close()
并且AllowClose
标志为false,我们可以保持流打开.然后,设置AllowClose
回true,正常的处理机制可以关闭它.
不要误会我的意思......这仍然是一个不应该被实现的黑客......但从内存使用的角度来看它有点清晰.
作为ALUN在上面的陈述,也这个问题,你可以喂流进入另一个MemoryStream的:
... MemoryStream ms = new MemoryStream(); using(MemoryStream tempStream = new MemoryStream) { workbook.Write(tempStream); var byteArray = tempStream.ToArray(); ms.Write(byteArray, 0, byteArray.Length); HttpResponseMessage result = new HttpResponseMessage(HttpStatusCode.OK); result.Content = new StreamContent(ms); result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment"); result.Content.Headers.ContentDisposition.FileName = string.Format("{0}.xlsx", dt.TableName); return result; }
不得不这样做有一点代码味道.但是,这仅在输出.xlsx文件时才有必要,因为第三方库涉及处理流的方式.