/// <summary> /// Creates a data element by reading the first multipart element or body of the request. /// </summary> private async Task <(Stream, DataElement)> ReadRequestAndCreateDataElementAsync(HttpRequest request, string elementType, List <Guid> refs, Instance instance) { DateTime creationTime = DateTime.UtcNow; Stream theStream; string contentType; string contentFileName = null; long fileSize = 0; if (MultipartRequestHelper.IsMultipartContentType(request.ContentType)) { // Only read the first section of the Multipart message. MediaTypeHeaderValue mediaType = MediaTypeHeaderValue.Parse(request.ContentType); string boundary = MultipartRequestHelper.GetBoundary(mediaType, _defaultFormOptions.MultipartBoundaryLengthLimit); MultipartReader reader = new MultipartReader(boundary, request.Body); MultipartSection section = await reader.ReadNextSectionAsync(); theStream = section.Body; contentType = section.ContentType; bool hasContentDisposition = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out ContentDispositionHeaderValue contentDisposition); if (hasContentDisposition) { contentFileName = HttpUtility.UrlDecode(contentDisposition.FileName.ToString()); fileSize = contentDisposition.Size ?? 0; } } else { theStream = request.Body; if (request.Headers.TryGetValue("Content-Disposition", out StringValues headerValues)) { string contentDisposition = headerValues.ToString(); List <string> contentDispositionValues = contentDisposition.Split(';').ToList(); string fileNameValue = contentDispositionValues.FirstOrDefault(x => x.Contains("filename", StringComparison.CurrentCultureIgnoreCase)); if (!string.IsNullOrEmpty(fileNameValue)) { string[] valueParts = fileNameValue.Split('='); if (valueParts.Length == 2) { contentFileName = HttpUtility.UrlDecode(valueParts[1]); } } } contentType = request.ContentType; } string user = User.GetUserOrOrgId(); DataElement newData = DataElementHelper.CreateDataElement(elementType, refs, instance, creationTime, contentType, contentFileName, fileSize, user); return(theStream, newData); }
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline. public void Configure(IApplicationBuilder app, IHostingEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseBrowserLink(); app.UseDatabaseErrorPage(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); app.UseAuthentication(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); app.Use(async(context, next) => { if (!IsMultipartContentType(context.Request.ContentType)) { await next(); return; } var boundary = GetBoundary(context.Request.ContentType); var reader = new MultipartReader(boundary, context.Request.Body); var section = await reader.ReadNextSectionAsync(); while (section != null) { // process each image const int chunkSize = 1024; var buffer = new byte[chunkSize]; var bytesRead = 0; var fileName = GetFileName(section.ContentDisposition); using (var stream = new FileStream(fileName, FileMode.Append)) { do { bytesRead = await section.Body.ReadAsync(buffer, 0, buffer.Length); stream.Write(buffer, 0, bytesRead); } while (bytesRead > 0); } section = await reader.ReadNextSectionAsync(); } context.Response.WriteAsync("Done."); }); }
/// <summary> /// Read the next multipart section /// </summary> /// <param name="reader">The reader to parse with.</param> /// <param name="async"> /// Whether to invoke the operation asynchronously. /// </param> /// <param name="cancellationToken"> /// Optional <see cref="CancellationToken"/> to propagate notifications /// that the operation should be cancelled. /// </param> /// <returns>The next multipart section.</returns> internal static async Task <MultipartSection> GetNextSectionAsync( this MultipartReader reader, bool async, CancellationToken cancellationToken) => async ? await reader.ReadNextSectionAsync(cancellationToken).ConfigureAwait(false) : #pragma warning disable AZC0102 reader.ReadNextSectionAsync(cancellationToken).GetAwaiter().GetResult(); // #7972: Decide if we need a proper sync API here
/// <summary> /// Read the next multipart section /// </summary> /// <param name="reader">The reader to parse with.</param> /// <param name="async"> /// Whether to invoke the operation asynchronously. /// </param> /// <param name="cancellationToken"> /// Optional <see cref="CancellationToken"/> to propagate notifications /// that the operation should be cancelled. /// </param> /// <returns>The next multipart section.</returns> internal static async Task <MultipartSection> GetNextSectionAsync( this MultipartReader reader, bool async, CancellationToken cancellationToken) => async ? await reader.ReadNextSectionAsync(cancellationToken).ConfigureAwait(false) : #pragma warning disable AZC0102 // Do not use GetAwaiter().GetResult(). Use the TaskExtensions.EnsureCompleted() extension method instead. reader.ReadNextSectionAsync(cancellationToken).GetAwaiter().GetResult(); // #7972: Decide if we need a proper sync API here
public async Task <IActionResult> UploadImages([FromRoute] string propertyId) { var boundary = GetBoundary(Request.ContentType); if (boundary == null) { if (await this.imageService.UploadFileToStorageAsync(Request.Body, propertyId, Guid.NewGuid().ToString())) { return(this.Ok()); } return(this.BadRequest()); } var reader = new MultipartReader(boundary, Request.Body, 80 * 1024); MultipartSection section; using (Stream stream = new MemoryStream()) { while ((section = await reader.ReadNextSectionAsync()) != null) { var contentDispo = section.GetContentDispositionHeader(); if (contentDispo.IsFileDisposition()) { var fileSection = section.AsFileSection(); var bufferSize = 32 * 1024; byte[] buffer = new byte[bufferSize]; if (stream.Position != 0) { return(BadRequest("Only one file is accepted per request.")); } await fileSection.FileStream.CopyToAsync(stream); } else if (contentDispo.IsFormDisposition()) { return(BadRequest("Only one file is accepted per request.")); } else { return(BadRequest("Malformatted message body.")); } } if (stream == null) { return(BadRequest("No file submitted.")); } stream.Seek(0, SeekOrigin.Begin); if (await this.imageService.UploadFileToStorageAsync(stream, propertyId, Guid.NewGuid().ToString())) { return(this.Ok()); } return(this.BadRequest()); } }
public async Task <Response> Handle(Request request, CancellationToken cancellationToken) { var httpContext = _httpContextAccessor.HttpContext; var defaultFormOptions = new FormOptions(); var digitalAssets = new List <DigitalAsset>(); if (!MultipartRequestHelper.IsMultipartContentType(httpContext.Request.ContentType)) { throw new Exception($"Expected a multipart request, but got {httpContext.Request.ContentType}"); } var mediaTypeHeaderValue = MediaTypeHeaderValue.Parse(httpContext.Request.ContentType); var boundary = MultipartRequestHelper.GetBoundary( mediaTypeHeaderValue, defaultFormOptions.MultipartBoundaryLengthLimit); var reader = new MultipartReader(boundary, httpContext.Request.Body); var section = await reader.ReadNextSectionAsync(); while (section != null) { DigitalAsset digitalAsset = default; var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out ContentDispositionHeaderValue contentDisposition); if (hasContentDispositionHeader) { if (MultipartRequestHelper.HasFileContentDisposition(contentDisposition)) { using (var targetStream = new MemoryStream()) { await section.Body.CopyToAsync(targetStream); var name = $"{contentDisposition.FileName}".Trim(new char[] { '"' }).Replace("&", "and"); var bytes = StreamHelper.ReadToEnd(targetStream); var contentType = section.ContentType; digitalAsset = new DigitalAsset(name, bytes, contentType); } } } _context.DigitalAssets.Add(digitalAsset); digitalAssets.Add(digitalAsset); section = await reader.ReadNextSectionAsync(); } await _context.SaveChangesAsync(cancellationToken); return(new() { DigitalAssetIds = digitalAssets.Select(x => x.DigitalAssetId).ToList() }); }
/// <summary> /// This api is to download the File from the Flite, This is called by intermediate service as FileReader Services. /// FileReader Services is read the file from flite and privide to this API by multipartFormDatacontent. /// </summary> /// <param name="id">File Id</param> /// <param name="fliteNo">Flite No</param> /// <param name="req">MultiPart Reader data consist of header, body etc...</param> /// <returns></returns> private async Task <FileUpload> UploadFile(string id, string fliteNo, HttpRequest req) { var boundary = HeaderUtilities.RemoveQuotes(MediaTypeHeaderValue.Parse(req.ContentType).Boundary); var reader = new MultipartReader(boundary.Value, req.Body); MultipartSection section = await reader.ReadNextSectionAsync(); string fileId = string.Empty; while (section != null) { ContentDispositionHeaderValue contentDisposition; var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out contentDisposition); if (string.IsNullOrEmpty(fileId) == false) { break; } fileId = $"NameofFile: {fliteNo}{id}"; var filePath = "FilePath" + fileId; _fileRep.Initialize(filePath); } try { var encoding = GetEncoding(section); using (var streamReader = new BinaryReader( section.Body, encoding, leaveOpen: true)) { while (true) { var bin = streamReader.ReadBytes(1024 * 1000 * 10); if (bin.Length == 0) { break; } using (MemoryStream ms = new MemoryStream(bin)) { _fileRep.UploadToBucket(ms); } } } } catch (Exception) { throw; } section = await reader.ReadNextSectionAsync(); return(new FileUpload() { FileId = fileId }); }
public void MultipartReader_BufferSizeMustBeLargerThanBoundary_Throws() { var stream = MakeStream(ThreePartBody); Assert.Throws <ArgumentOutOfRangeException>(() => { var reader = new MultipartReader(Boundary, stream, 5); }); }
public async Task <MultipartReader> GetMultipartReader(HttpRequest request) { FormOptions _defaultFormOptions = new FormOptions(); var boundary = await Task.Run(() => MultipartRequest.GetBoundary(MediaTypeHeaderValue.Parse(request.ContentType), _defaultFormOptions.MultipartBoundaryLengthLimit)); var reader = new MultipartReader(boundary, request.Body); return(reader); }
// expected exception public virtual void TestParseHeaders() { string testString = new string("\r\nFoo: Bar\r\n Header : Val ue "); MultipartReader reader = new MultipartReader("multipart/related;boundary=X", null ); reader.ParseHeaders(testString); NUnit.Framework.Assert.AreEqual(reader.headers.Keys.Count, 2); }
/// <summary> /// Copy file stream to <paramref name="targetStream" /> and creates and returns an instance of /// <typeparamref name="TDto" /> /// with properties fulfilled. /// </summary> /// <param name="controller"></param> /// <param name="targetStream"></param> /// <typeparam name="TDto"></typeparam> /// <returns>Instance of <typeparamref name="TDto" /></returns> /// <exception cref="Exception"></exception> /// <exception cref="InvalidDataException"></exception> /// <exception cref="InvalidOperationException"></exception> public static async Task <TDto> ReadMultiPartFormData <TDto>(this ControllerBase controller, Stream targetStream) where TDto : class, new() { if (IsMultipartContentType(controller.Request.ContentType) == false) { throw new Exception($"Expected a multipart request, but got {controller.Request.ContentType}"); } var boundary = MediaTypeHeaderValue.Parse(controller.Request.ContentType) .GetBoundary(DefaultFormOptions.MultipartBoundaryLengthLimit); var reader = new MultipartReader(boundary, controller.Request.Body); var formAccumulator = new KeyValueAccumulator(); var section = await reader.ReadNextSectionAsync(); do { if (ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition)) { if (contentDisposition.HasFile()) { await section.Body.CopyToAsync(targetStream); } else if (contentDisposition.HasFormData()) { var key = HeaderUtilities.RemoveQuotes(contentDisposition.Name); var encoding = GetEncoding(section); using (var streamReader = new StreamReader(section.Body, encoding)) { var value = await streamReader.ReadToEndAsync(); if (string.Equals(value, "undefined", StringComparison.OrdinalIgnoreCase)) { value = string.Empty; } formAccumulator.Append(key.Value.Replace("[]", string.Empty), value); if (formAccumulator.ValueCount > DefaultFormOptions.ValueCountLimit) { throw new InvalidDataException( $"Form key count limit {DefaultFormOptions.ValueCountLimit} exceeded."); } } } else { throw new InvalidOperationException("Content type invalid."); } } section = await reader.ReadNextSectionAsync(); } while (section != null); return(await GetModel <TDto>(controller, formAccumulator)); }
public async Task MultipartReader_StripQuotesFromBoundary() { var stream = MakeStream(OnePartBody); var reader = new MultipartReader(BoundaryWithQuotes, stream); var section = await reader.ReadNextSectionAsync(); Assert.NotNull(section); }
protected async Task <UploadModel> Upload() { var formOptions = new FormOptions(); var result = new UploadModel(); result.Boundary = MultipartRequestHelper.GetBoundary(MediaTypeHeaderValue.Parse(Request.ContentType), formOptions.MultipartBoundaryLengthLimit); var reader = new MultipartReader(result.Boundary, HttpContext.Request.Body); var section = (MultipartSection)null; while ((section = await reader.ReadNextSectionAsync()) != null) { if (ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition)) { if (MultipartRequestHelper.HasFile(contentDisposition)) { result.FileName = contentDisposition.FileName.ToString(); result.Stream = section.Body; return(result); } else if (MultipartRequestHelper.HasModel(contentDisposition)) { using (var factory = new DBItemConverterFactory(HttpContext)) { var option = new JsonSerializerOptions(); option.InitDefaults(factory); result.Model = await JsonSerializer.DeserializeAsync <T>(section.Body, option); } } else if (MultipartRequestHelper.HasFormData(contentDisposition)) { var key = HeaderUtilities.RemoveQuotes(contentDisposition.Name); var encoding = MultipartRequestHelper.GetEncoding(section); using (var streamReader = new StreamReader(section.Body, encoding, detectEncodingFromByteOrderMarks: true, bufferSize: 2048, leaveOpen: true)) { // The value length limit is enforced by MultipartBodyLengthLimit var value = await streamReader.ReadToEndAsync(); if (String.Equals(value, "undefined", StringComparison.OrdinalIgnoreCase)) { value = String.Empty; } if (StringSegment.Equals(key, "LastWriteTime", StringComparison.OrdinalIgnoreCase) && DateTime.TryParseExact(value, "o", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var lastWriteTime)) { result.ModificationDate = lastWriteTime; } else { result.Content[key.ToString()] = value; } } } } } return(result); }
internal async Task ImportStream(MultipartSection section) { var reader = new MultipartReader("++++++++", section.Body); var templateSection = await reader.ReadNextSectionAsync(); var template = await ReadTemplate(templateSection); var recordType = Assembly.GetAssembly(typeof(IBlazeEntity))?.ExportedTypes.FirstOrDefault(t => t.Name.Equals(template.TableName())); if (recordType == null) { throw new InvalidOperationException($"Cannot import records into object of type {template.TableName()}."); } var importSection = await reader.ReadNextSectionAsync(); using var stream = new StreamReader(importSection.Body); var config = template.CreateConfiguration(_logger); using var rdr = new CsvReader(stream, config); var records = rdr.GetRecords(recordType); switch (recordType.Name) { case "Portfolio": await _repository.SaveAsync(records.OfType <Portfolio>().ToList(), default, From); break; case "TaxLot": await _repository.SaveAsync(records.OfType <TaxLot>().ToList(), default, From); break; case "Security": await _repository.SaveAsync(records.OfType <Security>().ToList(), default, From); break; case "Order": await _repository.SaveAsync(records.OfType <Order>().ToList(), default, From); break; case "Model": await _repository.SaveAsync(records.OfType <Model>().ToList(), default, From); break; case "Allocation": await _repository.SaveAsync(records.OfType <Allocation>().ToList(), default, From); break; } }
public async Task <ResponseModel> Handle(MultipleFileUploadCommand request, CancellationToken cancellationToken) { var errorModel = new FormFileErrorModel(); if (!MultipartRequestHelper.IsMultipartContentType(_accessor.HttpContext.Request.ContentType)) { errorModel.Errors.Add("File", $"The request couldn't be processed (Error 1)."); return(ResponseProvider.Ok(errorModel)); } var boundary = MultipartRequestHelper.GetBoundary( MediaTypeHeaderValue.Parse(_accessor.HttpContext.Request.ContentType), _defaultFormOptions.MultipartBoundaryLengthLimit); var reader = new MultipartReader(boundary, _accessor.HttpContext.Request.Body); var section = await reader.ReadNextSectionAsync(cancellationToken); while (section != null) { var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse( section.ContentDisposition, out var contentDisposition); if (hasContentDispositionHeader) { if (!MultipartRequestHelper .HasFileContentDisposition(contentDisposition)) { errorModel.Errors.Add("File", $"The request couldn't be processed (Error 2)."); return(ResponseProvider.Ok(errorModel)); } else { var streamedFileContent = await FileHelpers.ProcessStreamedFile( section, contentDisposition, errorModel, _permittedExtensions, _streamFileLimitSize, ValidateExtension.Encrypt); if (errorModel.Errors.Any()) { return(ResponseProvider.Ok(errorModel)); } var fileName = FileHelpers.GetFileName(section.ContentDisposition); var fileNameWithEncryptExtension = UploadFileHelper.GetFileNameWithEncryptExtension(fileName, request.EncryptAlg); var uploadFileAbsolutePath = UploadFileHelper.GetUploadAbsolutePath(_contentRootPath, fileNameWithEncryptExtension, request.Archive); await UploadFile(streamedFileContent, uploadFileAbsolutePath, request.EncryptAlg); } } section = await reader.ReadNextSectionAsync(cancellationToken); } return(ResponseProvider.Ok("Upload file successfully")); }
public async Task <IActionResult> ReceiveFile() { if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType)) { throw new Exception("Not a multipart request"); } var boundary = MultipartRequestHelper.GetBoundary(MediaTypeHeaderValue.Parse(Request.ContentType), _defaultFormOptions.MultipartBoundaryLengthLimit); var reader = new MultipartReader(boundary, Request.Body); // note: this is for a single file, you could also process multiple files var section = await reader.ReadNextSectionAsync(); if (section == null) { return(BadRequest()); } Request.Headers.TryGetValue("Description", out Microsoft.Extensions.Primitives.StringValues videoDesecription); var i = 0; do { if (!ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition)) { throw new Exception("No content disposition in multipart defined"); } var fileName2 = contentDisposition.FileNameStar.ToString(); if (string.IsNullOrEmpty(fileName2)) { fileName2 = contentDisposition.FileName.ToString(); } using var fileStream = section.Body; var uri = await _azureRepo.NewBlobFromStream(fileStream, "videos", fileName2); await _videoRepo.AddVideo(new Media() { FullFileName = uri.ToString(), DisplayName = fileName2 + " " + videoDesecription, Type = contentDisposition.Name.Value }); //media.Type = "image/" + file.FileName.Split('.').Last(); //media.DisplayName = file.FileName; //media.FullFileName = "/upload/image/" + file.FileName; i++; section = await reader.ReadNextSectionAsync(); } while (section != null); //await SendFileSomewhere(fileStream); return(Ok()); }
public async Task <int> ReadMultipart() { var counter = 0; using (var body = GetPayload()) { var reader = new MultipartReader(Boundary, body); HttpApplicationRequestSection section; while ((section = await reader .ReadNextHttpApplicationRequestSectionAsync(default, false, CancellationToken.None)
private async Task StoreAsyncCore(FileIdentifier id, MultipartReader reader, CancellationToken cancellationToken) { // We need to manually read each part of the request. The browser may do as it likes and send whichever part first, // which means we have to build up the metadata incrementally and can only write it later UploadContext uploadContext = new UploadContext(id); StoredMetadata?metadata = null; try { MultipartSection section = await reader.ReadNextSectionAsync(cancellationToken); while (section != null) { await this.ProcessSectionAsync(uploadContext, section, cancellationToken); section = await reader.ReadNextSectionAsync(cancellationToken); } if (uploadContext.MetadataFactory.IsComplete()) { metadata = uploadContext.MetadataFactory.Build(); } if (metadata == null) { throw new InvalidOperationException("Metadata is incomplete - file is not uploaded or expiration missing"); } await this.StoreMetadataAsync(id, metadata, cancellationToken); this._logger.LogInformation(LogEvents.NewUpload, "Completed: New upload of file {0} to id {1} [{2:s}]", metadata.OriginalFileName, id, DateTime.UtcNow); } catch (OperationCanceledException ex) { this._logger.LogWarning(LogEvents.UploadCancelled, ex, "Upload failed due to cancellation"); this.TryCleanup(id); // No use rethrowing the exception, we're done anyway. } catch (InvalidDataException ex) { this._logger.LogError(LogEvents.UploadFailed, ex, "Upload failed due to file size exceeding"); this.TryCleanup(id); throw new UploadFailedException("File size exceeded of " + reader.BodyLengthLimit.GetValueOrDefault().Bytes().Megabytes); } catch (Exception ex) when(!(ex is UploadCryptoArgumentOrderException)) { this._logger.LogError(LogEvents.UploadFailed, ex, "Upload failed due to exception"); this.TryCleanup(id); throw new UploadFailedException("Unable to complete upload", ex); } }
public async Task <ActionResult <BlobInfo[]> > UploadAssetToLocalFileSystemAsync() { //ToDo Now supports downloading one file, find a solution for downloading multiple files // https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads?view=aspnetcore-3.1 var result = new List <BlobInfo>(); if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType)) { return(BadRequest($"Expected a multipart request, but got {Request.ContentType}")); } var uploadPath = Path.GetFullPath(_platformOptions.LocalUploadFolderPath); if (!Directory.Exists(uploadPath)) { Directory.CreateDirectory(uploadPath); } var boundary = MultipartRequestHelper.GetBoundary(MediaTypeHeaderValue.Parse(Request.ContentType), _defaultFormOptions.MultipartBoundaryLengthLimit); var reader = new MultipartReader(boundary, HttpContext.Request.Body); var section = await reader.ReadNextSectionAsync(); if (section != null) { var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition); if (hasContentDispositionHeader) { if (MultipartRequestHelper.HasFileContentDisposition(contentDisposition)) { var fileName = contentDisposition.FileName.Value; var targetFilePath = Path.Combine(uploadPath, fileName); if (!Directory.Exists(uploadPath)) { Directory.CreateDirectory(uploadPath); } using (var targetStream = System.IO.File.Create(targetFilePath)) { await section.Body.CopyToAsync(targetStream); } var blobInfo = AbstractTypeFactory <BlobInfo> .TryCreateInstance(); blobInfo.Name = fileName; //Use only file name as Url, for further access to these files need use PlatformOptions.LocalUploadFolderPath blobInfo.Url = fileName; blobInfo.ContentType = MimeTypeResolver.ResolveContentType(fileName); result.Add(blobInfo); } } } return(Ok(result.ToArray())); }
public async Task <Response> Handle(Request request, CancellationToken cancellationToken) { var digitalAssets = new HashSet <DigitalAssetApiModel>(); if (!MultipartRequestHelper.IsMultipartContentType(_httpContext.Request.ContentType)) { throw new Exception($"Expected a multipart request, but got {_httpContext.Request.ContentType}"); } var boundary = MultipartRequestHelper.GetBoundary( MediaTypeHeaderValue.Parse(_httpContext.Request.ContentType), _defaultFormOptions.MultipartBoundaryLengthLimit); var reader = new MultipartReader(boundary, _httpContext.Request.Body); var section = await reader.ReadNextSectionAsync(); while (section != null) { ContentDispositionHeaderValue contentDisposition; var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out contentDisposition); if (hasContentDispositionHeader) { if (MultipartRequestHelper.HasFileContentDisposition(contentDisposition)) { using (var targetStream = new MemoryStream()) { await section.Body.CopyToAsync(targetStream); var bytes = StreamHelper.ReadToEnd(targetStream); var tenant = await _context.Tenants.FindAsync(new Guid(_httpContext.Request.GetHeaderValue("Tenant"))); var digitalAsset = new DigitalAsset(); digitalAsset.Tenant = tenant; digitalAsset.FileName = $"{contentDisposition.FileName}".Trim(new char[] { '"' }).Replace("&", "and"); digitalAsset.Name = digitalAsset.FileName; digitalAsset.Bytes = bytes; digitalAsset.ContentType = section.ContentType; digitalAsset.UploadedOn = DateTime.UtcNow; _context.DigitalAssets.Add(digitalAsset); await _context.SaveChangesAsync(cancellationToken, request.Username); digitalAssets.Add(DigitalAssetApiModel.FromDigitalAsset(digitalAsset)); } } } section = await reader.ReadNextSectionAsync(); } return(new Response() { }); }
public async Task <IActionResult> UploadImageLarge() { try { if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType)) { return(BadRequest(string.Format(MessagesConstants.ExpectedDifferentRequest, Request.ContentType))); } // Used to accumulate all the form url encoded key value pairs in the // request. var formAccumulator = new KeyValueAccumulator(); string targetFilePath = null; var boundary = MultipartRequestHelper.GetBoundary( MediaTypeHeaderValue.Parse(Request.ContentType), _defaultFormOptions.MultipartBoundaryLengthLimit); var reader = new MultipartReader(boundary, HttpContext.Request.Body); var section = await reader.ReadNextSectionAsync(); while (section != null) { ContentDispositionHeaderValue contentDisposition; var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out contentDisposition); if (hasContentDispositionHeader) { if (MultipartRequestHelper.HasFileContentDisposition(contentDisposition)) { targetFilePath = Path.GetTempFileName(); using (var targetStream = System.IO.File.Create(targetFilePath)) { await section.Body.CopyToAsync(targetStream); } } else if (MultipartRequestHelper.HasFormDataContentDisposition(contentDisposition)) { formAccumulator = await UploadImageLargeRefactor(formAccumulator, section, contentDisposition); } } // Drains any remaining section body that has not been consumed and // reads the headers for the next section. section = await reader.ReadNextSectionAsync(); } return(Ok(new ApiResponse(Microsoft.AspNetCore.Http.StatusCodes.Status200OK, true, "Image Uploaded Successfully.", targetFilePath))); } catch (Exception ex) { HttpContext.RiseError(new Exception(string.Concat("API := (Image := UploadImageLarge)", ex.Message, " Stack Trace : ", ex.StackTrace, " Inner Exception : ", ex.InnerException))); return(Ok(someIssueInProcessing)); } }
internal AspNetCoreMultipartReader( string contentType, Stream body, ISeekableStreamConverter seekableStreamConverter, IOptions <StoreConfiguration> storeConfiguration) { EnsureArg.IsNotNull(contentType, nameof(contentType)); EnsureArg.IsNotNull(body, nameof(body)); EnsureArg.IsNotNull(seekableStreamConverter, nameof(seekableStreamConverter)); EnsureArg.IsNotNull(storeConfiguration?.Value, nameof(storeConfiguration)); _seekableStreamConverter = seekableStreamConverter; _storeConfiguration = storeConfiguration; if (!MediaTypeHeaderValue.TryParse(contentType, out MediaTypeHeaderValue media) || !media.MediaType.Equals(KnownContentTypes.MultipartRelated, StringComparison.InvariantCultureIgnoreCase)) { throw new UnsupportedMediaTypeException( string.Format(CultureInfo.InvariantCulture, DicomApiResource.UnsupportedContentType, contentType)); } string boundary = HeaderUtilities.RemoveQuotes(media.Boundary).ToString(); if (string.IsNullOrWhiteSpace(boundary)) { throw new UnsupportedMediaTypeException( string.Format(CultureInfo.InvariantCulture, DicomApiResource.InvalidMultipartContentType, contentType)); } // Check to see if the root content type was specified or not. if (media.Parameters != null) { foreach (NameValueHeaderValue parameter in media.Parameters) { if (TypeParameterName.Equals(parameter.Name.ToString(), StringComparison.OrdinalIgnoreCase)) { _rootContentType = HeaderUtilities.RemoveQuotes(parameter.Value).ToString(); } else if (StartParameterName.Equals(parameter.Name.ToString(), StringComparison.OrdinalIgnoreCase)) { // TODO: According to RFC2387 3.2, the root section can be specified by using the // start parameter. For now, we will assume that the first section is the "root" section // and will add support later. Throw exception in case start is specified. throw new NotSupportedException(DicomApiResource.StartParameterIsNotSupported); } } } _multipartReader = new MultipartReader(boundary, body) { // set the max length of each section in bytes BodyLengthLimit = _storeConfiguration.Value.MaxAllowedDicomFileSize, }; }
public void Configure(IApplicationBuilder app) { app.Use(async(context, next) => { if (!IsMultipartContentType(context.Request.ContentType)) { await next(); return; } var boundary = GetBoundary(context.Request.ContentType); var reader = new MultipartReader(boundary, context.Request.Body); var section = await reader.ReadNextSectionAsync(); while (section != null) { // process each image const int chunkSize = 1024; var buffer = new byte[chunkSize]; var bytesRead = 0; var fileName = GetFileName(section.ContentDisposition); using (var stream = new FileStream(fileName, FileMode.Append)) { do { bytesRead = await section.Body.ReadAsync(buffer, 0, buffer.Length); stream.Write(buffer, 0, bytesRead); } while (bytesRead > 0); } section = await reader.ReadNextSectionAsync(); } context.Response.WriteAsync("Done."); }); app.Run(async context => { context.Response.ContentType = "text/html"; await context.Response.WriteAsync("<html><body>"); await context.Response.WriteAsync(@" <FORM action = ""/"" method=""post"" enctype=""multipart/form-data"" > <LABEL for=""myfile1"">File 1:</LABEL> <INPUT type=""file"" name=""myfile1"" /><BR> <LABEL for=""myfile2"">File 2:</LABEL> <INPUT type=""file"" name=""myfile2"" /><BR> <INPUT type=""submit"" value=""Send"" /> </FORM>"); await context.Response.WriteAsync("</body></html>"); }); }
public async Task <IActionResult> UploadAsync() { if (!IsMultipartContentType(Request.ContentType)) { return(BadRequest($"Expected a multipart request, but got {Request.ContentType}")); } var formAccumulator = new KeyValueAccumulator(); var boundary = GetBoundary(MediaTypeHeaderValue.Parse(Request.ContentType), defaultFormOptions.MultipartBoundaryLengthLimit); var reader = new MultipartReader(boundary, Request.Body); var section = await reader.ReadNextSectionAsync(); var filesUploaded = new List <string>(); while (section != null) { var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition); if (hasContentDispositionHeader) { if (HasFileContentDisposition(contentDisposition)) { logger.LogInformation($"The file name of uploaded file is: {contentDisposition.FileName}"); await storageRepository.SaveDocument($"{Guid.NewGuid()}_{contentDisposition.FileName}", section.Body); } } else if (HasFormDataContentDisposition(contentDisposition)) { var key = HeaderUtilities.RemoveQuotes(contentDisposition.Name); var encoding = GetEncoding(section); using (var streamReader = new StreamReader(section.Body, encoding, true, 1024, true)) { var value = await streamReader.ReadToEndAsync(); if (string.Equals(value, "undefined", StringComparison.OrdinalIgnoreCase)) { value = string.Empty; } formAccumulator.Append(key.Value, value); if (formAccumulator.ValueCount > defaultFormOptions.ValueCountLimit) { throw new InvalidDataException($"Form key count limit {defaultFormOptions.ValueCountLimit} exceeded"); } } } section = await reader.ReadNextSectionAsync(); } return(Ok(new { Message = "File upload successful", Path = filesUploaded })); }
public static async System.Threading.Tasks.Task <ICollection <DigitalAsset> > Upload(IHttpContextAccessor httpContextAccessor, IAppDbContext context, CancellationToken cancellationToken) { var httpContext = httpContextAccessor.HttpContext; var defaultFormOptions = new FormOptions(); var digitalAssets = new List <DigitalAsset>(); if (!MultipartRequestHelper.IsMultipartContentType(httpContext.Request.ContentType)) { throw new Exception($"Expected a multipart request, but got {httpContext.Request.ContentType}"); } var mediaTypeHeaderValue = MediaTypeHeaderValue.Parse(httpContext.Request.ContentType); var boundary = MultipartRequestHelper.GetBoundary( mediaTypeHeaderValue, defaultFormOptions.MultipartBoundaryLengthLimit); var reader = new MultipartReader(boundary, httpContext.Request.Body); var section = await reader.ReadNextSectionAsync(); while (section != null) { var hasContentDispositionHeader = ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out ContentDispositionHeaderValue contentDisposition); if (hasContentDispositionHeader) { if (MultipartRequestHelper.HasFileContentDisposition(contentDisposition)) { using (var targetStream = new MemoryStream()) { await section.Body.CopyToAsync(targetStream, cancellationToken); var name = $"{contentDisposition.FileName}".Trim(new char[] { '"' }).Replace("&", "and"); var bytes = StreamHelper.ReadToEnd(targetStream); var contentType = section.ContentType; var digitalAsset = new DigitalAsset(name, bytes, contentType); context.Store(digitalAsset); digitalAssets.Add(digitalAsset); } } } section = await reader.ReadNextSectionAsync(cancellationToken); } await context.SaveChangesAsync(cancellationToken); return(digitalAssets); }
private async Task Upload(HttpContext context, string cmd, UserItem userItem) { try { var boundary = context.Request.GetMultipartBoundary(); if (string.IsNullOrWhiteSpace(boundary)) { await context.Response.WriteAsync(GetError(Api2Error.WrongRequest)); return; } string type = context.Request.Query["type"]; string item = context.Request.Query["item"]; // check type //Upload(string fileName, string description, Stream stream, string options) if (!DBManager.Instance.Get(type, out IManager manager)) { await context.Response.WriteAsync(GetError(Api2Error.Parameters)); return; } var reader = new MultipartReader(boundary, context.Request.Body); var section = await reader.ReadNextSectionAsync(); if (section == null) { await context.Response.WriteAsync(GetError(Api2Error.Parameters)); return; } var fileSection = section.AsFileSection(); var fileName = fileSection.FileName; int id = await manager.Upload(userItem, fileSection.FileName, fileSection.FileStream, item); var result = new Api2Result(cmd); result["id"] = id; await context.Response.WriteAsync(JsonConvert.SerializeObject(result)); } catch (Exception exc) { Logger.Instance.Save(exc); await context.Response.WriteAsync(GetError(Api2Error.Internal)); } }
public async Task MultipartReader_HeadersLengthExceeded_Throws() { var stream = MakeStream(OnePartBodyTwoHeaders); var reader = new MultipartReader(Boundary, stream) { HeadersLengthLimit = 60, }; var exception = await Assert.ThrowsAsync <InvalidDataException>(() => reader.ReadNextSectionAsync()); Assert.Equal("Line length limit 17 exceeded.", exception.Message); }
public async Task <string> UploadImage() { var re = Request; var apiKey = re.Headers.FirstOrDefault(x => x.Key.ToLower() == "x-api-key").Value; if (apiKey.ToString() != _realKey) { throw new ArgumentException("Invalid or no API Key provided (x-api-key header)"); } var boundary = Request.GetMultipartBoundary(); var reader = new MultipartReader(boundary, Request.Body); var section = await reader.ReadNextSectionAsync(); var disposition = section.GetContentDispositionHeader(); if (disposition != null) { var fileSection = section.AsFileSection(); var contentType = section.ContentType; // Generate random filename string hash; using (var md5 = MD5.Create()) { hash = BitConverter.ToString(md5.ComputeHash(Encoding.ASCII.GetBytes(fileSection.FileName))).Replace("-", "").ToLower(); } var timestamp = new DateTimeOffset(DateTime.UtcNow).ToUnixTimeMilliseconds().ToString(); var filename = _generateFilename(disposition.FileName.ToString()); filename += timestamp.Substring(timestamp.Length - 2); filename += hash.Substring(0, 2); var extension = MimeTypesMap.GetExtension(contentType); filename += $".{extension}"; string path = Path.Combine(_storagePath, $"{filename}"); using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write)) { fileSection.FileStream.Position = 0; await fileSection.FileStream.CopyToAsync(fileStream); } return($"{_url}/{filename}"); } else { throw new ArgumentException("No Content-Disposition header"); } }
public void MultipartReader_HeaderCountExceeded_Throws() { var stream = MakeStream(OnePartBodyTwoHeaders); var reader = new MultipartReader(Boundary, stream) { HeadersCountLimit = 1, }; var exception = Assert.ThrowsAsync <InvalidDataException>(async() => await reader.ReadNextSectionAsync()); Assert.That(exception.Message, Is.EqualTo("Multipart headers count limit 1 exceeded.")); }
public async Task <string> ValidateImportRequestData(HttpRequest request) { FormOptions _defaultFormOptions = new FormOptions(); var accumulator = new KeyValueAccumulator(); string filePath = Path.GetTempFileName(); 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)) { using (var targetStream = File.Create(filePath)) { await section.Body.CopyToAsync(targetStream); } } else if (MultipartRequest.HasFormDataContentDisposition(contentDisposition)) { var key = HeaderUtilities.RemoveQuotes(contentDisposition.Name).ToString(); var encoding = GetEncoding(section); using (var streamReader = new StreamReader( section.Body, encoding, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true)) { var value = await streamReader.ReadToEndAsync(); if (String.Equals(value, "undefined", StringComparison.OrdinalIgnoreCase)) { value = String.Empty; } accumulator.Append(key, value); if (accumulator.ValueCount > _defaultFormOptions.ValueCountLimit) { throw new InvalidDataException($"For key count limit {_defaultFormOptions.ValueCountLimit} exceeded"); } } } } section = await reader.ReadNextSectionAsync(); } return(filePath); }
private async Task<IFormCollection> InnerReadFormAsync(CancellationToken cancellationToken) { if (!HasFormContentType) { throw new InvalidOperationException("Incorrect Content-Type: " + _request.ContentType); } cancellationToken.ThrowIfCancellationRequested(); if (_options.BufferBody) { _request.EnableRewind(_options.MemoryBufferThreshold, _options.BufferBodyLengthLimit); } FormCollection formFields = null; FormFileCollection files = null; // Some of these code paths use StreamReader which does not support cancellation tokens. using (cancellationToken.Register((state) => ((HttpContext)state).Abort(), _request.HttpContext)) { var contentType = ContentType; // Check the content-type if (HasApplicationFormContentType(contentType)) { var encoding = FilterEncoding(contentType.Encoding); using (var formReader = new FormReader(_request.Body, encoding) { ValueCountLimit = _options.ValueCountLimit, KeyLengthLimit = _options.KeyLengthLimit, ValueLengthLimit = _options.ValueLengthLimit, }) { formFields = new FormCollection(await formReader.ReadFormAsync(cancellationToken)); } } else if (HasMultipartFormContentType(contentType)) { var formAccumulator = new KeyValueAccumulator(); var boundary = GetBoundary(contentType, _options.MultipartBoundaryLengthLimit); var multipartReader = new MultipartReader(boundary, _request.Body) { HeadersCountLimit = _options.MultipartHeadersCountLimit, HeadersLengthLimit = _options.MultipartHeadersLengthLimit, BodyLengthLimit = _options.MultipartBodyLengthLimit, }; var section = await multipartReader.ReadNextSectionAsync(cancellationToken); while (section != null) { ContentDispositionHeaderValue contentDisposition; ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out contentDisposition); if (HasFileContentDisposition(contentDisposition)) { // Enable buffering for the file if not already done for the full body section.EnableRewind(_request.HttpContext.Response.RegisterForDispose, _options.MemoryBufferThreshold, _options.MultipartBodyLengthLimit); // Find the end await section.Body.DrainAsync(cancellationToken); var name = HeaderUtilities.RemoveQuotes(contentDisposition.Name) ?? string.Empty; var fileName = HeaderUtilities.RemoveQuotes(contentDisposition.FileName) ?? string.Empty; FormFile file; if (section.BaseStreamOffset.HasValue) { // Relative reference to buffered request body file = new FormFile(_request.Body, section.BaseStreamOffset.Value, section.Body.Length, name, fileName); } else { // Individually buffered file body file = new FormFile(section.Body, 0, section.Body.Length, name, fileName); } file.Headers = new HeaderDictionary(section.Headers); if (files == null) { files = new FormFileCollection(); } if (files.Count >= _options.ValueCountLimit) { throw new InvalidDataException($"Form value count limit {_options.ValueCountLimit} exceeded."); } files.Add(file); } else if (HasFormDataContentDisposition(contentDisposition)) { // Content-Disposition: form-data; name="key" // // value // Do not limit the key name length here because the mulipart headers length limit is already in effect. var key = HeaderUtilities.RemoveQuotes(contentDisposition.Name); MediaTypeHeaderValue mediaType; MediaTypeHeaderValue.TryParse(section.ContentType, out mediaType); var encoding = FilterEncoding(mediaType?.Encoding); using (var reader = new StreamReader(section.Body, encoding, detectEncodingFromByteOrderMarks: true, bufferSize: 1024, leaveOpen: true)) { // The value length limit is enforced by MultipartBodyLengthLimit var value = await reader.ReadToEndAsync(); formAccumulator.Append(key, value); if (formAccumulator.ValueCount > _options.ValueCountLimit) { throw new InvalidDataException($"Form value count limit {_options.ValueCountLimit} exceeded."); } } } else { System.Diagnostics.Debug.Assert(false, "Unrecognized content-disposition for this section: " + section.ContentDisposition); } section = await multipartReader.ReadNextSectionAsync(cancellationToken); } if (formAccumulator.HasValues) { formFields = new FormCollection(formAccumulator.GetResults(), files); } } } // Rewind so later readers don't have to. if (_request.Body.CanSeek) { _request.Body.Seek(0, SeekOrigin.Begin); } if (formFields != null) { Form = formFields; } else if (files != null) { Form = new FormCollection(null, files); } else { Form = FormCollection.Empty; } return Form; }
public async Task MutipartReader_HeadersLengthExceeded_Throws() { var stream = MakeStream(OnePartBodyTwoHeaders); var reader = new MultipartReader(Boundary, stream) { HeadersLengthLimit = 60, }; var exception = await Assert.ThrowsAsync<InvalidDataException>(() => reader.ReadNextSectionAsync()); Assert.Equal("Line length limit 17 exceeded.", exception.Message); }
public async Task MutipartReader_ReadSinglePartBodyWithoutLastCRLF_Success() { var stream = MakeStream(OnePartBodyWithoutFinalCRLF); var reader = new MultipartReader(Boundary, stream); var section = await reader.ReadNextSectionAsync(); Assert.NotNull(section); Assert.Equal(1, section.Headers.Count); Assert.Equal("form-data; name=\"text\"", section.Headers["Content-Disposition"][0]); var buffer = new MemoryStream(); await section.Body.CopyToAsync(buffer); Assert.Equal("text default", Encoding.ASCII.GetString(buffer.ToArray())); Assert.Null(await reader.ReadNextSectionAsync()); }
public void MutipartReader_BufferSizeMustBeLargerThanBoundary_Throws() { var stream = MakeStream(ThreePartBody); Assert.Throws<ArgumentOutOfRangeException>(() => { var reader = new MultipartReader(Boundary, stream, 5); }); }
public async Task MutipartReader_TwoPartBodyIncompleteBuffer_TwoSectionsReadSuccessfullyThirdSectionThrows() { var stream = MakeStream(TwoPartBodyIncompleteBuffer); var reader = new MultipartReader(Boundary, stream); var buffer = new byte[128]; //first section can be read successfully var section = await reader.ReadNextSectionAsync(); Assert.NotNull(section); Assert.Equal(1, section.Headers.Count); Assert.Equal("form-data; name=\"text\"", section.Headers["Content-Disposition"][0]); var read = section.Body.Read(buffer, 0, buffer.Length); Assert.Equal("text default", GetString(buffer, read)); //second section can be read successfully (even though the bottom boundary is truncated) section = await reader.ReadNextSectionAsync(); Assert.NotNull(section); Assert.Equal(2, section.Headers.Count); Assert.Equal("form-data; name=\"file1\"; filename=\"a.txt\"", section.Headers["Content-Disposition"][0]); Assert.Equal("text/plain", section.Headers["Content-Type"][0]); read = section.Body.Read(buffer, 0, buffer.Length); Assert.Equal("Content of a.txt.\r\n", GetString(buffer, read)); await Assert.ThrowsAsync<IOException>(async () => { // we'll be unable to ensure enough bytes are buffered to even contain a final boundary section = await reader.ReadNextSectionAsync(); }); }
public async Task MutipartReader_ReadTwoPartBodyWithUnicodeFileName_Success() { var stream = MakeStream(TwoPartBodyWithUnicodeFileName); var reader = new MultipartReader(Boundary, stream); var section = await reader.ReadNextSectionAsync(); Assert.NotNull(section); Assert.Equal(1, section.Headers.Count); Assert.Equal("form-data; name=\"text\"", section.Headers["Content-Disposition"][0]); var buffer = new MemoryStream(); await section.Body.CopyToAsync(buffer); Assert.Equal("text default", Encoding.ASCII.GetString(buffer.ToArray())); section = await reader.ReadNextSectionAsync(); Assert.NotNull(section); Assert.Equal(2, section.Headers.Count); Assert.Equal("form-data; name=\"file1\"; filename=\"a色.txt\"", section.Headers["Content-Disposition"][0]); Assert.Equal("text/plain", section.Headers["Content-Type"][0]); buffer = new MemoryStream(); await section.Body.CopyToAsync(buffer); Assert.Equal("Content of a.txt.\r\n", Encoding.ASCII.GetString(buffer.ToArray())); Assert.Null(await reader.ReadNextSectionAsync()); }
public async Task MutipartReader_ThreePartBody_Success() { var stream = MakeStream(ThreePartBody); var reader = new MultipartReader(Boundary, stream); var section = await reader.ReadNextSectionAsync(); Assert.NotNull(section); Assert.Equal(1, section.Headers.Count); Assert.Equal("form-data; name=\"text\"", section.Headers["Content-Disposition"][0]); var buffer = new MemoryStream(); await section.Body.CopyToAsync(buffer); Assert.Equal("text default", Encoding.ASCII.GetString(buffer.ToArray())); section = await reader.ReadNextSectionAsync(); Assert.NotNull(section); Assert.Equal(2, section.Headers.Count); Assert.Equal("form-data; name=\"file1\"; filename=\"a.txt\"", section.Headers["Content-Disposition"][0]); Assert.Equal("text/plain", section.Headers["Content-Type"][0]); buffer = new MemoryStream(); await section.Body.CopyToAsync(buffer); Assert.Equal("Content of a.txt.\r\n", Encoding.ASCII.GetString(buffer.ToArray())); section = await reader.ReadNextSectionAsync(); Assert.NotNull(section); Assert.Equal(2, section.Headers.Count); Assert.Equal("form-data; name=\"file2\"; filename=\"a.html\"", section.Headers["Content-Disposition"][0]); Assert.Equal("text/html", section.Headers["Content-Type"][0]); buffer = new MemoryStream(); await section.Body.CopyToAsync(buffer); Assert.Equal("<!DOCTYPE html><title>Content of a.html.</title>\r\n", Encoding.ASCII.GetString(buffer.ToArray())); Assert.Null(await reader.ReadNextSectionAsync()); }
public async Task MutipartReader_ReadInvalidUtf8SurrogateHeader_ReplacementCharacters() { var body1 = "--9051914041544843365972754266\r\n" + "Content-Disposition: form-data; name=\"text\" filename=\"a"; var body2 = ".txt\"\r\n" + "\r\n" + "text default\r\n" + "--9051914041544843365972754266--\r\n"; var stream = new MemoryStream(); var bytes = Encoding.UTF8.GetBytes(body1); stream.Write(bytes, 0, bytes.Length); // Write an invalid utf-8 segment in the middle stream.Write(new byte[] { 0xED, 0xA0, 85 }, 0, 3); bytes = Encoding.UTF8.GetBytes(body2); stream.Write(bytes, 0, bytes.Length); stream.Seek(0, SeekOrigin.Begin); var reader = new MultipartReader(Boundary, stream); var section = await reader.ReadNextSectionAsync(); Assert.NotNull(section); Assert.Equal(1, section.Headers.Count); Assert.Equal("form-data; name=\"text\" filename=\"a\uFFFDU.txt\"", section.Headers["Content-Disposition"][0]); var buffer = new MemoryStream(); await section.Body.CopyToAsync(buffer); Assert.Equal("text default", Encoding.ASCII.GetString(buffer.ToArray())); Assert.Null(await reader.ReadNextSectionAsync()); }