private void WriteMultipleContent(Stream stream, IHttpContent content, Action <UploadStatusMessage> uploadStatusCallback, int blockSize) { long contentLength = content.GetContentLength(); long totalContentUploaded = 0; MultipartContent multipartContent = content as MultipartContent; foreach (IHttpContent singleContent in multipartContent) { stream.Write(multipartContent.BoundaryStartBytes, 0, multipartContent.BoundaryStartBytes.Length); totalContentUploaded += multipartContent.BoundaryStartBytes.Length; foreach (var header in singleContent.Headers) { byte[] headerBytes = Encoding.UTF8.GetBytes(header.Key + ": " + header.Value); stream.Write(headerBytes, 0, headerBytes.Length); totalContentUploaded += headerBytes.Length; stream.Write(multipartContent.CRLFBytes, 0, multipartContent.CRLFBytes.Length); totalContentUploaded += multipartContent.CRLFBytes.Length; } stream.Write(multipartContent.CRLFBytes, 0, multipartContent.CRLFBytes.Length); totalContentUploaded += multipartContent.CRLFBytes.Length; totalContentUploaded += WriteSingleContent(stream, singleContent, uploadStatusCallback, blockSize, contentLength, totalContentUploaded); stream.Write(multipartContent.CRLFBytes, 0, multipartContent.CRLFBytes.Length); totalContentUploaded += multipartContent.CRLFBytes.Length; } if (!multipartContent.Any()) { stream.Write(multipartContent.BoundaryStartBytes, 0, multipartContent.BoundaryStartBytes.Length); totalContentUploaded += multipartContent.BoundaryStartBytes.Length; } stream.Write(multipartContent.BoundaryEndBytes, 0, multipartContent.BoundaryEndBytes.Length); totalContentUploaded += multipartContent.BoundaryEndBytes.Length; RaiseUploadStatusCallback(uploadStatusCallback, contentLength, (multipartContent.CRLFBytes.Length * 2) + multipartContent.BoundaryEndBytes.Length, totalContentUploaded); }
/// <summary> /// <see cref="HttpContent"/> implementation which provides a byte range view over a stream used to generate HTTP /// 206 (Partial Content) byte range responses. If none of the requested ranges overlap with the current extend /// of the selected resource represented by the <paramref name="content"/> parameter then an /// <see cref="InvalidByteRangeException"/> is thrown indicating the valid Content-Range of the content. /// </summary> /// <param name="content">The stream over which to generate a byte range view.</param> /// <param name="range">The range or ranges, typically obtained from the Range HTTP request header field.</param> /// <param name="mediaType">The media type of the content stream.</param> /// <param name="bufferSize">The buffer size used when copying the content stream.</param> public ByteRangeStreamContent(Stream content, RangeHeaderValue range, MediaTypeHeaderValue mediaType, int bufferSize) { try { // If we have more than one range then we use a multipart/byteranges content type as wrapper. // Otherwise we use a non-multipart response. if (range.Ranges.Count > 1) { // Create Multipart content and copy headers to this content MultipartContent rangeContent = new MultipartContent(ByteRangesContentSubtype); _byteRangeContent = rangeContent; foreach (RangeItemHeaderValue rangeValue in range.Ranges) { try { ByteRangeStream rangeStream = new ByteRangeStream(content, rangeValue); HttpContent rangeBodyPart = new StreamContent(rangeStream, bufferSize); rangeBodyPart.Headers.ContentType = mediaType; rangeBodyPart.Headers.ContentRange = rangeStream.ContentRange; rangeContent.Add(rangeBodyPart); } catch (ArgumentOutOfRangeException) { // We ignore range errors until we check that we have at least one valid range } } // If no overlapping ranges were found then stop if (!rangeContent.Any()) { ContentRangeHeaderValue actualContentRange = new ContentRangeHeaderValue(content.Length); string msg = "ByteRangeStreamNoneOverlap"; throw new InvalidByteRangeException(actualContentRange, msg); } } else if (range.Ranges.Count == 1) { try { ByteRangeStream rangeStream = new ByteRangeStream(content, range.Ranges.First()); _byteRangeContent = new StreamContent(rangeStream, bufferSize); _byteRangeContent.Headers.ContentType = mediaType; _byteRangeContent.Headers.ContentRange = rangeStream.ContentRange; } catch (ArgumentOutOfRangeException) { ContentRangeHeaderValue actualContentRange = new ContentRangeHeaderValue(content.Length); string msg = "ByteRangeStreamNoOverlap"; throw new InvalidByteRangeException(actualContentRange, msg); } } else { throw new ArgumentException("range"); } // Copy headers from byte range content so that we get the right content type etc. _byteRangeContent.Headers.CopyTo(Headers); _content = content; _start = content.Position; } catch { if (_byteRangeContent != null) { _byteRangeContent.Dispose(); } throw; } }
/// <summary> /// <see cref="HttpContent"/> implementation which provides a byte range view over a stream used to generate HTTP /// 206 (Partial Content) byte range responses. If none of the requested ranges overlap with the current extend /// of the selected resource represented by the <paramref name="content"/> parameter then an /// <see cref="InvalidByteRangeException"/> is thrown indicating the valid Content-Range of the content. /// </summary> /// <param name="content">The stream over which to generate a byte range view.</param> /// <param name="range">The range or ranges, typically obtained from the Range HTTP request header field.</param> /// <param name="mediaType">The media type of the content stream.</param> /// <param name="bufferSize">The buffer size used when copying the content stream.</param> public ByteRangeStreamContent(Stream content, RangeHeaderValue range, MediaTypeHeaderValue mediaType, int bufferSize) { if (content == null) { throw new ArgumentNullException("content"); } if (!content.CanSeek) { throw new ArgumentException("content", RS.Format(Resources.ByteRangeStreamNotSeekable, typeof(ByteRangeStreamContent).Name)); } if (range == null) { throw new ArgumentNullException("range"); } if (mediaType == null) { throw new ArgumentNullException("mediaType"); } if (bufferSize < MinBufferSize) { throw new ArgumentOutOfRangeException("bufferSize", bufferSize, RS.Format(Resources.ArgumentMustBeGreaterThanOrEqualTo, MinBufferSize)); } if (!range.Unit.Equals(SupportedRangeUnit, StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException(RS.Format(Resources.ByteRangeStreamContentNotBytesRange, range.Unit, SupportedRangeUnit), "range"); } try { // If we have more than one range then we use a multipart/byteranges content type as wrapper. // Otherwise we use a non-multipart response. if (range.Ranges.Count > 1) { // Create Multipart content and copy headers to this content MultipartContent rangeContent = new MultipartContent(ByteRangesContentSubtype); _byteRangeContent = rangeContent; foreach (RangeItemHeaderValue rangeValue in range.Ranges) { try { ByteRangeStream rangeStream = new ByteRangeStream(content, rangeValue); HttpContent rangeBodyPart = new StreamContent(rangeStream, bufferSize); rangeBodyPart.Headers.ContentType = mediaType; rangeBodyPart.Headers.ContentRange = rangeStream.ContentRange; rangeContent.Add(rangeBodyPart); } catch (ArgumentOutOfRangeException) { // We ignore range errors until we check that we have at least one valid range } } // If no overlapping ranges were found then stop if (!rangeContent.Any()) { ContentRangeHeaderValue actualContentRange = new ContentRangeHeaderValue(content.Length); string msg = RS.Format(Resources.ByteRangeStreamNoneOverlap, range.ToString()); throw new InvalidByteRangeException(actualContentRange, msg); } } else if (range.Ranges.Count == 1) { try { ByteRangeStream rangeStream = new ByteRangeStream(content, range.Ranges.First()); _byteRangeContent = new StreamContent(rangeStream, bufferSize); _byteRangeContent.Headers.ContentType = mediaType; _byteRangeContent.Headers.ContentRange = rangeStream.ContentRange; } catch (ArgumentOutOfRangeException) { ContentRangeHeaderValue actualContentRange = new ContentRangeHeaderValue(content.Length); string msg = RS.Format(Resources.ByteRangeStreamNoOverlap, range.ToString()); throw new InvalidByteRangeException(actualContentRange, msg); } } else { throw new ArgumentException(Resources.ByteRangeStreamContentNoRanges, "range"); } // Copy headers from byte range content so that we get the right content type etc. foreach (KeyValuePair<string, IEnumerable<string>> header in _byteRangeContent.Headers) { Headers.TryAddWithoutValidation(header.Key, header.Value); } _content = content; _start = content.Position; } catch { if (_byteRangeContent != null) { _byteRangeContent.Dispose(); } throw; } }
/// <summary> /// <see cref="HttpContent"/> implementation which provides a byte range view over a stream used to generate HTTP /// 206 (Partial Content) byte range responses. If none of the requested ranges overlap with the current extend /// of the selected resource represented by the <paramref name="content"/> parameter then an /// <see cref="InvalidByteRangeException"/> is thrown indicating the valid Content-Range of the content. /// </summary> /// <param name="content">The stream over which to generate a byte range view.</param> /// <param name="range">The range or ranges, typically obtained from the Range HTTP request header field.</param> /// <param name="mediaType">The media type of the content stream.</param> /// <param name="bufferSize">The buffer size used when copying the content stream.</param> public ByteRangeStreamContent(Stream content, RangeHeaderValue range, MediaTypeHeaderValue mediaType, int bufferSize) { if (content == null) { throw Error.ArgumentNull("content"); } if (!content.CanSeek) { throw Error.Argument("content", Resources.ByteRangeStreamNotSeekable, typeof(ByteRangeStreamContent).Name); } if (range == null) { throw Error.ArgumentNull("range"); } if (mediaType == null) { throw Error.ArgumentNull("mediaType"); } if (bufferSize < MinBufferSize) { throw Error.ArgumentMustBeGreaterThanOrEqualTo("bufferSize", bufferSize, MinBufferSize); } if (!range.Unit.Equals(SupportedRangeUnit, StringComparison.OrdinalIgnoreCase)) { throw Error.Argument("range", Resources.ByteRangeStreamContentNotBytesRange, range.Unit, SupportedRangeUnit); } try { // If we have more than one range then we use a multipart/byteranges content type as wrapper. // Otherwise we use a non-multipart response. if (range.Ranges.Count > 1) { // Create Multipart content and copy headers to this content MultipartContent rangeContent = new MultipartContent(ByteRangesContentSubtype); this._byteRangeContent = rangeContent; foreach (RangeItemHeaderValue rangeValue in range.Ranges) { try { ByteRangeStream rangeStream = new ByteRangeStream(content, rangeValue); HttpContent rangeBodyPart = new StreamContent(rangeStream, bufferSize); rangeBodyPart.Headers.ContentType = mediaType; rangeBodyPart.Headers.ContentRange = rangeStream.ContentRange; rangeContent.Add(rangeBodyPart); } catch (ArgumentOutOfRangeException) { // We ignore range errors until we check that we have at least one valid range } } // If no overlapping ranges were found then stop if (!rangeContent.Any()) { ContentRangeHeaderValue actualContentRange = new ContentRangeHeaderValue(content.Length); string msg = Error.Format(Resources.ByteRangeStreamNoneOverlap, range.ToString()); throw new InvalidByteRangeException(actualContentRange, msg); } } else if (range.Ranges.Count == 1) { try { ByteRangeStream rangeStream = new ByteRangeStream(content, range.Ranges.First()); this._byteRangeContent = new StreamContent(rangeStream, bufferSize); this._byteRangeContent.Headers.ContentType = mediaType; this._byteRangeContent.Headers.ContentRange = rangeStream.ContentRange; } catch (ArgumentOutOfRangeException) { ContentRangeHeaderValue actualContentRange = new ContentRangeHeaderValue(content.Length); string msg = Error.Format(Resources.ByteRangeStreamNoOverlap, range.ToString()); throw new InvalidByteRangeException(actualContentRange, msg); } } else { throw Error.Argument("range", Resources.ByteRangeStreamContentNoRanges); } // Copy headers from byte range content so that we get the right content type etc. this._byteRangeContent.Headers.CopyTo(this.Headers); this._content = content; this._start = content.Position; } catch { if (this._byteRangeContent != null) { this._byteRangeContent.Dispose(); } throw; } }