user808359
user808359

Reputation: 31

how to buffer an input stream until it is complete

i'm implementing a wcf service that accepts image streams. however i'm currently getting an exception when i run it. as its trying to get the length of the stream before the stream is complete. so what i'd like to do is buffer the stream until its complete. however i cant find any examples of how to do this...

can anyone help?

my code so far:

    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);
    }

Upvotes: 2

Views: 6388

Answers (4)

Ma Dude
Ma Dude

Reputation: 557

Jon Skeets answer for .Net 3.5 and below using a Buffer Read is actually done incorrectly.

The buffer isn't cleared between reads which can result in issues on any read that returns less than 8192, for example if the 2nd read, read 192 bytes, the 8000 last bytes from the first read would STILL be in the buffer which would then be returned to the stream.

My code below you supply it a Stream and it will return a IEnumerable array.
Using this you can for-each it and Write to a MemoryStream and then use .GetBuffer() to end up with a compiled merged byte[].

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)
}

Upvotes: -1

Hugh Jones
Hugh Jones

Reputation: 2694

Try this :

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

Upvotes: 0

user153923
user153923

Reputation:

I'm not sure what you are returning (or not returning), but something like this might work for you:

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();
}

A string can hold up to 2 GB, I believe.

Upvotes: 1

Jon Skeet
Jon Skeet

Reputation: 1502556

Rather than try to fetch the length, you should read from the stream until it returns that it's "done". In .NET 4, this is really easy:

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

In .NET 3.5 there's no CopyTo method, but you can write something similar yourself:

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);
    }
}

However, now we've got something to copy a stream, why bother reading it all into memory first? Let's just write it straight to a file:

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

Upvotes: 9

Related Questions