165 lines
6.5 KiB
C#
165 lines
6.5 KiB
C#
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
|
|
|
|
/// <summary>
|
|
/// Handle processing uplod form with potentially huge files being uploaded (which means can't use simplest built in upload handler method)
|
|
/// </summary>
|
|
internal static class ApiUploadProcessor
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// handle upload
|
|
/// </summary>
|
|
/// <param name="httpContext"></param>
|
|
/// <returns><see cref="ApiUploadedFilesResult"/> list of files and form field data (if present)</returns>
|
|
internal static async Task<ApiUploadedFilesResult> ProcessUploadAsync(Microsoft.AspNetCore.Http.HttpContext httpContext)
|
|
{
|
|
|
|
ApiUploadedFilesResult result = new ApiUploadedFilesResult();
|
|
FormOptions _defaultFormOptions = new FormOptions();
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
|
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.UTF7.Equals(mediaType.Encoding))
|
|
{
|
|
return Encoding.UTF8;
|
|
}
|
|
return mediaType.Encoding;
|
|
}
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
/// Contains result of upload form processor
|
|
/// </summary>
|
|
public class ApiUploadedFilesResult
|
|
{
|
|
public Dictionary<string, Microsoft.Extensions.Primitives.StringValues> FormFieldData { get; set; }
|
|
public List<UploadedFileInfo> UploadedFiles { get; set; }
|
|
|
|
public ApiUploadedFilesResult()
|
|
{
|
|
FormFieldData = new Dictionary<string, Microsoft.Extensions.Primitives.StringValues>();
|
|
UploadedFiles = new List<UploadedFileInfo>();
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}//eoc
|
|
|
|
|
|
}//eons
|