【问题标题】:how to buffer an input stream until it is complete如何缓冲输入流直到完成
【发布时间】:2011-10-12 18:05:43
【问题描述】:

我正在实现一个接受图像流的 wcf 服务。但是,当我运行它时,我目前遇到了异常。因为它试图在流完成之前获取流的长度。所以我想做的是缓冲流直到它完成。但是我找不到任何如何做到这一点的例子......

谁能帮忙?

到目前为止我的代码:

    public String uploadUserImage(Stream stream)
    {
          Stream fs = stream;

          BinaryReader br = new BinaryReader(fs);

          Byte[] bytes = br.ReadBytes((Int32)fs.Length);// this causes exception

          File.WriteAllBytes(filepath, bytes);
    }

【问题讨论】:

  • 异常类型和消息是什么?

标签: c# wcf stream


【解决方案1】:

与其尝试获取长度,不如从流中读取,直到它返回“完成”。在 .NET 4 中,这非常简单:

// Assuming we *really* want to read it into memory first...
MemoryStream memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
memoryStream.Position = 0;
File.WriteAllBytes(filepath, memoryStream);

在 .NET 3.5 中没有 CopyTo 方法,但您可以自己编写类似的东西:

public static void CopyStream(Stream input, Stream output)
{
    byte[] buffer = new byte[8192];
    int bytesRead;
    while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
    {
        output.Write(buffer, 0, bytesRead);
    }
}

但是,现在我们已经有了复制流的东西,为什么还要先把它全部读入内存呢?让我们直接将其写入文件:

using (FileStream output = File.OpenWrite(filepath))
{
    CopyStream(stream, output); // Or stream.CopyTo(output);
}

【讨论】:

  • 不断收到:在 System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath) 异常...
  • 第一个解决方案完美运行第二个(对我来说更有吸引力)不会自动结束
  • @ruedi:“不会自动结束”是什么意思?应该没问题。我不确定为什么您更喜欢第二种解决方案而不是第一种解决方案 - 使用现有的 CopyTo 方法肯定比自己编写更简单。
  • 我不想将它读入内存,这就是原因。第一个代码运行通过(我的示例数据为 2 秒),我可以看到数据已写入文本文件。第二个代码不会停止。如果我手动停止它,我可以看到数据也被写入文件。
  • @ruedi:啊,我明白了。我以为你的意思是CopyStream 方法而不是CopyTo。如果不了解更多有关您正在阅读的内容,就很难知道发生了什么 - 听起来您要么有一个没有结束的流,要么Write 调用被阻止。如果没有更多上下文,我们无能为力 - 我建议您提出一个新问题。
【解决方案2】:

我不确定您要返回(或不返回)什么,但这样的事情可能对您有用:

public String uploadUserImage(Stream stream) {
  const int KB = 1024;
  Byte[] bytes = new Byte[KB];
  StringBuilder sb = new StringBuilder();
  using (BinaryReader br = new BinaryReader(stream)) {
    int len;
    do {
      len = br.Read(bytes, 0, KB);
      string readData = Encoding.UTF8.GetString(bytes);
      sb.Append(readData);
    } while (len == KB);
  }
  //File.WriteAllBytes(filepath, bytes);
  return sb.ToString();
}

我相信,一个字符串最多可以容纳 2 GB。

【讨论】:

    【解决方案3】:

    试试这个:

        using (StreamWriter sw = File.CreateText(filepath))
        {
            stream.CopyTo(sw);
            sw.Close();
        }
    

    【讨论】:

    • 为此,您必须确保源位于开头。 Stream.Seek() 将为您提供帮助。
    【解决方案4】:

    Jon Skeets 对 .Net 3.5 及更低版本使用缓冲区读取的回答实际上是错误的。

    两次读取之间没有清除缓冲区,这可能会导致任何返回小于 8192 的读取出现问题,例如,如果第二次读取读取 192 个字节,则第一次读取的最后 8000 个字节仍会在缓冲区中然后将其返回到流中。

    我下面的代码为它提供了一个 Stream,它将返回一个 IEnumerable 数组。
    使用它,您可以 for-each 它并写入 MemoryStream,然后使用 .GetBuffer() 最终得到一个已编译的合并字节 []。

    private IEnumerable<byte[]> ReadFullStream(Stream stream) {
       while(true) {
            byte[] buffer = new byte[8192];//since this is created every loop, its buffer is cleared
            int bytesRead = stream.Read(buffer, 0, buffer.Length);//read up to 8192 bytes into buffer
            if (bytesRead == 0) {//if we read nothing, stream is finished
                break;
            }
            if(bytesRead < buffer.Length) {//if we read LESS than 8192 bytes, resize the buffer to essentially remove everything after what was read, otherwise you will have nullbytes/0x00bytes at the end of your buffer
                Array.Resize(ref buffer, bytesRead);
            }
            yield return buffer;//yield return the buffer data
        }//loop here until we reach a read == 0 (end of stream)
    }
    

    【讨论】:

    • 不,我的回答没有错。我专门只将已读取的字节数写入流:output.Write(buffer, 0, bytesRead);。从未使用过“剩余”数据。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-30
    • 1970-01-01
    • 2016-07-14
    • 2016-12-23
    • 1970-01-01
    • 2020-09-10
    • 1970-01-01
    相关资源
    最近更新 更多