private static void ValidateMultipartRequest(HttpRequest request) { if (!MultipartRequest.IsMultipartContentType(request.ContentType)) { throw new InvalidOperationException($"Expected a multipart request, but got {request.ContentType}"); } }
public static async Task <FormValueProvider> StreamFile(this HttpRequest request, Stream targetStream) { ValidateMultipartRequest(request); // Used to accumulate all the form url encoded key value pairs in the // request. var formAccumulator = new KeyValueAccumulator(); var boundary = MultipartRequest.GetBoundary(MediaTypeHeaderValue.Parse(request.ContentType), defaultFormOptions.MultipartBoundaryLengthLimit); var reader = new MultipartReader(boundary, request.Body); var section = await reader.ReadNextSectionAsync(); while (section != null) { var hasContentDispositionHeader = ContentDispositionHeaderValue .TryParse(section.ContentDisposition, out ContentDispositionHeaderValue contentDisposition); if (hasContentDispositionHeader) { if (MultipartRequest.HasFileContentDisposition(contentDisposition)) { formAccumulator.Append("FileContentType", section.ContentType); formAccumulator.Append("FileName", contentDisposition.FileName.Value); await section.Body.CopyToAsync(targetStream); targetStream.Seek(0, SeekOrigin.Begin); } else { if (MultipartRequest.HasFormDataContentDisposition(contentDisposition)) { // Content-Disposition: form-data; name="key" // // value // Do not limit the key name length here because the // multipart headers length limit is already in effect. var key = HeaderUtilities.RemoveQuotes(contentDisposition.Name); var encoding = GetEncoding(section); using (var streamReader = new StreamReader( section.Body, encoding, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true)) { // The value length limit is enforced by MultipartBodyLengthLimit var value = await ReadValue(streamReader); formAccumulator.Append(key.Value, value); // For .NET Core <2.0 remove ".Value" from key CheckIfFormKeyLimitExceeded(formAccumulator); } } } } // Drains any remaining section body that has not been consumed and // reads the headers for the next section. section = await reader.ReadNextSectionAsync(); } // Bind form data to a model var formValueProvider = new FormValueProvider( BindingSource.Form, new FormCollection(formAccumulator.GetResults()), CultureInfo.CurrentCulture); return(formValueProvider); }