内存流问题

5

我在使用MemoryStream时遇到了一些问题。

我们正在使用类似Excel的第三方电子表格组件,我正在尝试将数据保存为html,这可以通过下面的SaveHtml方法进行支持。根据他们的文档,这似乎非常简单。以下是我的代码:

using (var memoryStream = new MemoryStream())
{
   this.ActiveSheet.SaveHtml(memoryStream);

   memoryStream.Position = 0;

   using (var streamReader = new StreamReader(memoryStream))
   {
       var htmlData = streamReader.ReadToEnd();
   }
}

当我设置memoryStream.Position = 0时,出现了异常:

System.ObjectDisposedException: 无法访问已关闭的流。

通过反编译器快速查看他们的SaveHtml方法,发现以下相关代码:

public void SaveHtml(Stream stream)
{
    StreamWriter writer = null;

    try
    {
        writer = new StreamWriter(stream) { AutoFlush = true };

        writer.Write(str);
    }   
    finally
    {
        if (writer != null)
        {
            writer.Close();
        }
    }
}

我猜测是因为他们的代码关闭了streamWriter,所以我们没有办法了。关闭streamWriter会同时关闭底层的Stream,对吗?
有什么绕过这个问题的方法吗?
谢谢。

3
根据我的看法,memoryStream.GetBuffer() 应该仍然有效。 - user1096188
根据来源,应该可以!回答时请发表。好好记住,谢谢 ;) - leppie
2个回答

6

看起来它能够正常工作,所以你可以创建另一个内存流。当提取缓冲区时不会复制任何字节。

using (var memoryStream = new MemoryStream()) {
    this.ActiveSheet.SaveHtml(memoryStream);
    var buffer = memoryStream.GetBuffer();
    using (var memoryStream2 = new MemoryStream(buffer))
    using (var streamReader = new StreamReader(memoryStream2)) {
        var htmlData = streamReader.ReadToEnd();
    }
}

或者,您可以自己创建一个不可关闭的MemoryStream并将其提供给SaveHtml。这样也可以防止对其进行处理,因为Dispose只会调用Close。再次强调:
class MemoryStream2 : MemoryStream {
    public override void Close() { }
}

1
.GetBuffer() 可能会在末尾返回垃圾字节。请改用 .ToArray()(即使流已关闭也适用)。而且,您可以直接调用 Encoding.UTF8.GetString(memoryStream.ToArray()),而无需创建另一个内存流。 - Allon Guralnek
@user1096188:最好重写Dispose。无论如何,重写是个好主意 :) +1 - leppie
@Allon 你确定会有垃圾吗?我试过几次运行 Console.WriteLine(new MemoryStream(1024 * 1024 * 1024).GetBuffer().Any(b => b != 0));,它总是打印 false... - user1096188
缓冲区保证被零初始化。我认为Allon的观点是缓冲区可能比流的内容更大。如果流在某个时候被缩短,那么“额外”的字节可能是非零的。在这种情况下,我不认为这是一个风险。此外,虽然我不确定StreamReader如何处理末尾的额外零字节,但我认为它可以很好地处理它们。 - phoog

0

在这种情况下,可以使用替代的内存流实现:

var stream = new ChunkedStream(pool, asOutputStreamOnDispose: true);

using (var writer = new StreamWriter(stream))
{
    writer.Write("hello world");
}

Assert.AreEqual(0, stream.Position);
Assert.AreEqual(ChunkedStreamState.ReadForward, stream.State);
Assert.AreEqual(3, pool.TotalAllocated);

using (var reader = new StreamReader(stream))
{
    Assert.AreEqual("hello world", reader.ReadToEnd());
    Assert.AreEqual(0, pool.TotalAllocated);
}

Assert.AreEqual(ChunkedStreamState.Closed, stream.State);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接