MemoryStream似乎在NPOI workbook.write后closures?

我正在使用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; } 

我在workbook.Write(ms)之后设置了一个检查ms.Length的断点,但它返回一个exception: System.ObjectDisposedException

我哪里做错了?

解决此问题的另一个解决方法…它不使用多个MemoryStream对象。

创build一个inheritanceMemoryStreamNpoiMemoryStream类,并重写Close方法:

 public class NpoiMemoryStream : MemoryStream { public NpoiMemoryStream() { AllowClose = true; } public bool AllowClose { get; set; } public override void Close() { if (AllowClose) base.Close(); } } 

然后,使用这样的stream:

 var ms = new NpoiMemoryStream(); ms.AllowClose = false; workbook.Write(ms); ms.Flush(); ms.Seek(0, SeekOrigin.Begin); ms.AllowClose = true; 

在刷新和search之间的某个点,NPOI将尝试closuresstream,但是由于我们覆盖Close()并且AllowClose标志为false,我们可以保持stream打开。 然后,将AllowClose设置为true,以便正常的处理机制可以closures它。

不要误解我的意思……这仍然是一个不应该被实施的黑客行为,但是从内存使用的angular度来看,它有点干净。

正如alun上面所述,也是在这个问题中,您可以将stream传送到另一个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文件时必须的,因为涉及的第三方库处理stream。

我遇到了closures/处理它们不拥有的stream的API的类似问题。 我不熟悉NPOI,但我认为Write方法正在接受Stream,而不是MemoryStream。 如果是这样的话,你可以创build一个包装stream类,它将所有调用(读/写/查找等)转发到内部stream(在这种情况下是你的MemoryStream),但不转发调用closures/处理。 将包装传递给Write方法,当它返回时,你的MemoryStream应该包含所有的内容,并且仍然是“打开”的。

此外,您可能需要ms.Seek(0, SeekOrigin.Begin) 。 调用Write之后,你的内存stream将被定位在stream的末尾,所以如果你尝试从那个位置读取它将会显示出来。