【问题标题】:Returning a stream from File.OpenRead()从 File.OpenRead() 返回一个流
【发布时间】:2012-02-03 05:12:05
【问题描述】:

我正在编写允许 ASP.Net 网站检索文件的 WCF 服务(基于this article)。我的问题是当我返回流时,它是空白的。

为简单起见,我将代码隔离到一个简单的 winforms 应用程序中,以尝试找出返回流的问题所在,代码如下:

    private Stream TestStream()
    {
        Stream fs = File.OpenRead(@"c:\testdocument.docx");
        return fs;
    }

    // This method converts the filestream into a byte array so that when it is 
    // used in my ASP.Net project the file can be sent using response.Write
    private void Test()
    {            
        System.IO.MemoryStream data = new System.IO.MemoryStream();
        System.IO.Stream str = TestStream();

        str.CopyTo(data);
        byte[] buf = new byte[data.Length];
        data.Read(buf, 0, buf.Length);                       
    }

这段代码的结果是buf 的长度为 12,587 字节(文件的正确长度),但它只包含 0。

如果我尝试一下,Word 文档会毫无问题地打开,我是否遗漏了一些明显的东西?

【问题讨论】:

  • 您是否以管理员身份运行?尝试从“我的文档”或 root 以外的其他文件夹中提取文档。
  • @keyboard - 很好的建议,但它会产生异常,而不是 0s 和正确的长度。
  • @HenkHolterman - 是的,没错。
  • 我在本地运行这个测试代码,所以这应该不是问题。我已经使用流式阅读器进行了快速测试,它可以毫无问题地访问文件。

标签: c# filestream


【解决方案1】:

你忘了寻找:

str.CopyTo(data);
data.Seek(0, SeekOrigin.Begin); // <-- missing line
byte[] buf = new byte[data.Length];
data.Read(buf, 0, buf.Length);

【讨论】:

  • 如果不允许搜索怎么办?
【解决方案2】:

选项:

  • 按照 ken2k 的建议使用 data.Seek
  • 使用更简单的Position 属性:

    data.Position = 0;
    
  • 使用MemoryStream 中的ToArray 调用让您的生活更简单开始:

    byte[] buf = data.ToArray();
    

第三个选项是我的首选方法。

请注意,您应该有一个 using 语句来自动关闭文件流(对于 MemoryStream 是可选的),我会为 System.IO 添加一个 using 指令以使您的代码更清晰:

byte[] buf;
using (MemoryStream data = new MemoryStream())
{
    using (Stream file = TestStream())
    {
        file.CopyTo(data);
        buf = data.ToArray();
    }
}

// Use buf

您可能还想在 Stream 上创建一个扩展方法,以便在一个地方为您执行此操作,例如

public static byte[] CopyToArray(this Stream input)
{
    using (MemoryStream memoryStream = new MemoryStream())
    {
        input.CopyTo(memoryStream);
        return memoryStream.ToArray();
    }
}

请注意,这不会关闭输入流。

【讨论】:

  • 感谢 Jon,这是一种更好的方法。 WCF 服务确实使用了 using 语句,我只是在测试代码中省略了它们,以便在这里发布时更容易阅读。
【解决方案3】:

你忘了重置内存流的位置:

private void Test()
{            
    System.IO.MemoryStream data = new System.IO.MemoryStream();
    System.IO.Stream str = TestStream();

    str.CopyTo(data);
    // Reset memory stream
    data.Seek(0, SeekOrigin.Begin);
    byte[] buf = new byte[data.Length];
    data.Read(buf, 0, buf.Length);                       
}

更新:

还有一件事需要注意:通常不要忽略方法的返回值。更健壮的实现应该检查调用返回后读取了多少字节:

private void Test()
{            
    using(MemoryStream data = new MemoryStream())
    {
        using(Stream str = TestStream())
        {
           str.CopyTo(data);
        }
        // Reset memory stream
        data.Seek(0, SeekOrigin.Begin);
        byte[] buf = new byte[data.Length];
        int bytesRead = data.Read(buf, 0, buf.Length);

        Debug.Assert(bytesRead == data.Length, 
                    String.Format("Expected to read {0} bytes, but read {1}.",
                        data.Length, bytesRead));
    }                     
}

【讨论】:

    【解决方案4】:

    你需要

        str.CopyTo(data);
        data.Position = 0; // reset to beginning
        byte[] buf = new byte[data.Length];
        data.Read(buf, 0, buf.Length);  
    

    并且由于您的 Test() 方法正在模仿客户端,它应该 Close()Dispose() str 流。还有memoryStream,只是出于本金。

    【讨论】:

    • 感谢 Henk,效果很好。 WCF 代码将相应地关闭/处理流,我在测试应用程序中忽略了这一点。
    • 我收集了,但这使它成为一个不完整的测试应用程序。
    • 同意,但该应用程序仅用于测试/调试问题,而不是测试工具或任何正式测试中涉及的任何东西。我更容易将代码移植到一个简单的应用程序中,然后尝试在其服务器上调试 WCF 服务。
    【解决方案5】:

    尝试将您的代码更改为:

    private void Test()
    {            
        System.IO.MemoryStream data = new System.IO.MemoryStream(TestStream());
    
        byte[] buf = new byte[data.Length];
        data.Read(buf, 0, buf.Length);                       
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-12-08
      • 2012-04-19
      • 1970-01-01
      • 2021-08-18
      • 1970-01-01
      • 1970-01-01
      • 2022-01-05
      • 1970-01-01
      相关资源
      最近更新 更多