[〜#〜] npoi [〜#〜] を使用して、ASP.NET Web APIプロジェクトでDataTableを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
_を検査しましたが、例外が返されました:_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;
_
フラッシュとシークの間のある時点で、NPOIはストリームを閉じようとしますが、Close()
をオーバーライドし、AllowClose
フラグがfalseであるため、ストリームを開いたままにすることができます。次に、AllowClose
をtrueに戻して、通常の廃棄メカニズムでクローズできるようにします。
誤解しないでください...これはまだ実装する必要のないハックです...しかし、メモリ使用の観点からは少しクリーンです。
上記の alun および this の質問でも、ストリームを別の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ファイルを出力する場合にのみ必要です。
所有していないストリームを閉じる/破棄するAPIで同様の問題が発生しました。私はNPOIに精通していませんが、WriteメソッドはMemoryStreamではなくStreamを受け入れていると思います。その場合は、すべての呼び出し(読み取り/書き込み/シークなど)を内部ストリーム(この場合はMemoryStream)に転送するが、呼び出しをclose/disposeに転送しないラッパーStreamクラスを作成できます。ラッパーをWriteメソッドに渡します。これが返されると、MemoryStreamはすべてのコンテンツを含み、まだ「開いている」はずです。
さらに、おそらくms.Seek(0, SeekOrigin.Begin)
が必要になります。 Writeの呼び出し後、メモリストリームはストリームの最後に配置されるため、その位置から読み取ろうとすると、空になります。
これがまだ必要かどうかはわかりませんが、overload
があります
Write(Stream stream, bool leaveOpen)
ここで、leaveOpen = true
、MemoryStreamを開いたままにします