Created
March 7, 2011 08:06
-
-
Save thecodejunkie/858210 to your computer and use it in GitHub Desktop.
Read HTTP multipart/form-data formatted streams
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public class Boundary | |
| { | |
| private readonly SubStream boundaryStream; | |
| private const byte LF = (byte)'\n'; | |
| private const byte CR = (byte)'\r'; | |
| public Boundary(SubStream boundaryStream) | |
| { | |
| this.boundaryStream = boundaryStream; | |
| this.ExtractHeaders(); | |
| } | |
| public string ContentType { get; set; } | |
| public string Filename { get; set; } | |
| public string Name { get; set; } | |
| public Stream Value { get; set; } | |
| private void ExtractHeaders() | |
| { | |
| while(true) | |
| { | |
| var header = | |
| this.ReadLineFromStream(); | |
| if (string.IsNullOrEmpty(header)) | |
| { | |
| break; | |
| } | |
| if (header.StartsWith("Content-Disposition", StringComparison.CurrentCultureIgnoreCase)) | |
| { | |
| this.Name = Regex.Match(header, @"name=""(?<name>[^\""]*)", RegexOptions.IgnoreCase).Groups["name"].Value; | |
| this.Filename = Regex.Match(header, @"filename=""(?<filename>[^\""]*)", RegexOptions.IgnoreCase).Groups["filename"].Value; | |
| } | |
| if (header.StartsWith("Content-Type", StringComparison.InvariantCultureIgnoreCase)) | |
| { | |
| this.ContentType = header.Split(new[] { ' ' }).Last().Trim(); | |
| } | |
| } | |
| this.boundaryStream.PositionStartAtCurrentLocation(); | |
| } | |
| private string ReadLineFromStream() | |
| { | |
| var readBuffer = new StringBuilder(); | |
| while (true) | |
| { | |
| var byteReadFromStream = this.boundaryStream.ReadByte(); | |
| if (byteReadFromStream == -1) | |
| return null; | |
| if (byteReadFromStream.Equals(LF)) | |
| break; | |
| readBuffer.Append((char)byteReadFromStream); | |
| } | |
| var lineReadFromStream = | |
| readBuffer.ToString().Trim(new[] { (char)CR }); | |
| return lineReadFromStream; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public class Buffer | |
| { | |
| private readonly byte[] boundaryAsBytes; | |
| private readonly byte[] buffer; | |
| private int position; | |
| public Buffer(byte[] boundaryAsBytes) | |
| { | |
| this.boundaryAsBytes = boundaryAsBytes; | |
| this.buffer = new byte[this.boundaryAsBytes.Length]; | |
| } | |
| public bool IsBoundary | |
| { | |
| get { return this.buffer.SequenceEqual(this.boundaryAsBytes); } | |
| } | |
| public bool IsFull | |
| { | |
| get { return this.position.Equals(this.buffer.Length); } | |
| } | |
| public int Length | |
| { | |
| get { return this.buffer.Length; } | |
| } | |
| public void Clear() | |
| { | |
| this.position = 0; | |
| } | |
| public void Insert(byte value) | |
| { | |
| this.buffer[this.position++] = value; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| public class Multipart | |
| { | |
| private const byte LF = (byte)'\n'; | |
| private readonly byte[] boundaryAsBytes; | |
| private Buffer readBuffer; | |
| private readonly Stream requestStream; | |
| public Multipart(Stream requestStream, string boundary) | |
| { | |
| this.requestStream = requestStream; | |
| this.boundaryAsBytes = GetBoundaryAsBytes(boundary); | |
| this.readBuffer = new Buffer(this.boundaryAsBytes); | |
| } | |
| public IEnumerable<Boundary> GetBoundaries() | |
| { | |
| return | |
| from boundaryStream in this.GetBoundarySubStreams() | |
| select new Boundary(boundaryStream); | |
| } | |
| private IEnumerable<SubStream> GetBoundarySubStreams() | |
| { | |
| var boundarySubStreams = new List<SubStream>(); | |
| var boundaryStart = this.GetNextBoundaryPosition(); | |
| while (boundaryStart > -1) | |
| { | |
| var boundaryEnd = this.GetNextBoundaryPosition(); | |
| boundarySubStreams.Add(new SubStream( | |
| this.requestStream, | |
| boundaryStart, | |
| this.GetActualEndOfBoundary(boundaryEnd))); | |
| boundaryStart = boundaryEnd; | |
| } | |
| return boundarySubStreams; | |
| } | |
| private long GetActualEndOfBoundary(long boundaryEnd) | |
| { | |
| if (this.CheckIfFoundEndOfStream()) | |
| { | |
| return this.requestStream.Position; | |
| } | |
| return boundaryEnd - (this.readBuffer.Length + 2); | |
| } | |
| private bool CheckIfFoundEndOfStream() | |
| { | |
| return this.requestStream.Position.Equals(this.requestStream.Length); | |
| } | |
| private static byte[] GetBoundaryAsBytes(string boundary) | |
| { | |
| var boundaryBuilder = new StringBuilder(); | |
| boundaryBuilder.Append("--"); | |
| boundaryBuilder.Append(boundary); | |
| boundaryBuilder.Append('\r'); | |
| boundaryBuilder.Append('\n'); | |
| var bytes = | |
| Encoding.ASCII.GetBytes(boundaryBuilder.ToString()); | |
| return bytes; | |
| } | |
| private long GetNextBoundaryPosition() | |
| { | |
| this.readBuffer.Clear(); | |
| while(true) | |
| { | |
| var byteReadFromStream = this.requestStream.ReadByte(); | |
| if (byteReadFromStream == -1) | |
| { | |
| return -1; | |
| } | |
| this.readBuffer.Insert((byte)byteReadFromStream); | |
| if (this.readBuffer.IsFull && this.readBuffer.IsBoundary) | |
| { | |
| return this.requestStream.Position; | |
| } | |
| if (byteReadFromStream.Equals(LF) || this.readBuffer.IsFull) | |
| { | |
| this.readBuffer.Clear(); | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment