using System; using System.Threading.Tasks; using System.IO; using System.Text; using Microsoft.AspNetCore.Http.Features; using Microsoft.AspNetCore.WebUtilities; using Microsoft.Net.Http.Headers; using System.Collections.Generic; using AyaNova.Models; using AyaNova.Util; namespace AyaNova.Api.ControllerHelpers { //Adapted from the example found here: https://docs.microsoft.com/en-us/aspnet/core/mvc/models/file-uploads#uploading-large-files-with-streaming //See AttachmentController at bottom of class for example of form that works with this code /// /// Handle processing uplod form with potentially huge files being uploaded (which means can't use simplest built in upload handler method) /// internal static class ApiUploadProcessor { /// /// handle upload /// /// /// list of files and form field data (if present) internal static async Task ProcessUploadAsync(Microsoft.AspNetCore.Http.HttpContext httpContext) { ApiUploadedFilesResult result = new ApiUploadedFilesResult(); FormOptions _defaultFormOptions = new FormOptions(); try { // Used to accumulate all the form url encoded key value pairs in the // request. var formAccumulator = new KeyValueAccumulator(); 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)) { string filePathAndName = string.Empty; var CleanedUploadFileName = contentDisposition.FileName.Value.Replace("\"", ""); //get temp file path and temp file name filePathAndName = FileUtil.NewRandomAttachmentFilesFolderFileName; //save to disk using (var stream = new FileStream(filePathAndName, FileMode.Create)) { await section.Body.CopyToAsync(stream); } result.UploadedFiles.Add(new UploadedFileInfo() { InitialUploadedPathName = filePathAndName, OriginalFileName = CleanedUploadFileName, MimeType = section.ContentType }); } else if (MultipartRequestHelper.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 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."); } } } } // Drains any remaining section body that has not been consumed and // reads the headers for the next section. section = await reader.ReadNextSectionAsync(); } //Get any extra form fields and return them result.FormFieldData = formAccumulator.GetResults(); return result; } catch (Microsoft.AspNetCore.Http.BadHttpRequestException ex) { //most commonly here due to file too large result.Error = $"Code:{ex.StatusCode}, Error: {ex.Message}"; return result; } } public static void DeleteTempUploadFile(ApiUploadedFilesResult uploadFormData) { if (uploadFormData.UploadedFiles.Count > 0) { foreach (UploadedFileInfo a in uploadFormData.UploadedFiles) { System.IO.File.Delete(a.InitialUploadedPathName); } } } private static Encoding GetEncoding(MultipartSection section) { MediaTypeHeaderValue mediaType; var hasMediaTypeHeader = MediaTypeHeaderValue.TryParse(section.ContentType, out mediaType); // UTF-7 is insecure and should not be honored. UTF-8 will succeed in // most cases. if (!hasMediaTypeHeader || Encoding.UTF8.Equals(mediaType.Encoding)) { return Encoding.UTF8; } return mediaType.Encoding; } /// /// Contains result of upload form processor /// public class ApiUploadedFilesResult { public Dictionary FormFieldData { get; set; } public List UploadedFiles { get; set; } public string Error { get; set; } public ApiUploadedFilesResult() { FormFieldData = new Dictionary(); UploadedFiles = new List(); Error = null; } } }//eoc }//eons