Skip to content

Instantly share code, notes, and snippets.

@grumpydev
Created March 15, 2011 08:42
Show Gist options
  • Save grumpydev/870467 to your computer and use it in GitHub Desktop.
Save grumpydev/870467 to your computer and use it in GitHub Desktop.
public class RequestStream : Stream
{
private Stream stream;
private readonly long expectedLength;
private readonly long threshHoldLength;
private readonly bool disableStreamSwapping;
public RequestStream(long expectedLength, long threshHoldLength, bool disableStreamSwapping)
: this(null, expectedLength, threshHoldLength, disableStreamSwapping)
{
}
public RequestStream(Stream stream, long expectedLength, long threshHoldLength, bool disableStreamSwapping)
{
this.expectedLength = expectedLength;
this.threshHoldLength = threshHoldLength;
this.disableStreamSwapping = disableStreamSwapping;
if (stream == null)
{
stream = this.CreateStream();
}
if (!stream.CanRead)
throw new InvalidOperationException();
if (!stream.CanSeek)
throw new InvalidOperationException();
if (expectedLength < 0)
throw new ArgumentOutOfRangeException();
if (threshHoldLength < 0)
throw new ArgumentOutOfRangeException();
this.stream = stream;
this.stream.Position = 0;
// If we know up front that we are likely to get more that we
// want to keep in memory, we should switch over to FileStream immediatly
if (this.expectedLength > this.threshHoldLength)
{
this.MoveContentsToFileStream();
}
}
private Stream CreateStream()
{
if (this.disableStreamSwapping || this.expectedLength < this.threshHoldLength)
{
return new MemoryStream(this.expectedLength);
}
this.disableStreamSwapping = true;
return this.CreateTemporaryFileStream();
}
private void MoveContentsToFileStream()
{
// If we've explicitly been instructed not to flip the stream then be
// nice and comply
if (this.disableStreamSwapping)
{
return;
}
var temporaryFileStream = CreateTemporaryFileStream();
// If the is no need to copy stuff, just replace it
if (this.stream.Length == 0)
{
this.stream.Close(); // Is this really needed?
this.stream = temporaryFileStream;
return;
}
this.stream.CopyTo(temporaryFileStream);
this.stream.Flush(); // Need to do this (apparently)
this.stream.Close(); // Is this really needed?
this.stream = temporaryFileStream;
}
private static FileStream CreateTemporaryFileStream()
{
var path = CreateTemporaryFileStreamName();
return new FileStream(path, FileMode.Create, FileAccess.ReadWrite);
}
private static string CreateTemporaryFileStreamName()
{
var fileName = Path.GetTempFileName();
return Path.Combine(Path.GetTempPath(), fileName);
}
public override void Flush()
{
this.stream.Flush();
}
public override long Seek(long offset, SeekOrigin origin)
{
return this.stream.Seek(offset, origin);
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override int Read(byte[] buffer, int offset, int count)
{
return this.stream.Read(buffer, offset, count);
}
public override int ReadByte()
{
return this.stream.ReadByte();
}
public override void Write(byte[] buffer, int offset, int count)
{
this.stream.Write(buffer, offset, count);
// Don't bother checking stream lengths if we don't have to
if (this.disableStreamSwapping)
{
return;
}
// If we've written enough into the
if (this.stream.Length >= this.threshHoldLength)
{
this.MoveContentsToFileStream();
}
}
public bool IsFileStream
{
get { return this.stream.GetType().Equals(typeof(FileStream)); }
}
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return true; }
}
public override bool CanTimeout
{
get { return false; }
}
public override bool CanWrite
{
get { return false; }
}
public override long Length
{
get { return this.stream.Length; }
}
public override long Position
{
get { return this.stream.Position; }
set
{
if (value < 0)
throw new ArgumentOutOfRangeException();
if (value > this.Length)
throw new ArgumentOutOfRangeException();
this.Position = value;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment