Skip to content

Instantly share code, notes, and snippets.

@zhengchun
Created March 22, 2019 01:59
Show Gist options
  • Save zhengchun/1ae317ee3fb623da4ae3fd9faa52d6a0 to your computer and use it in GitHub Desktop.
Save zhengchun/1ae317ee3fb623da4ae3fd9faa52d6a0 to your computer and use it in GitHub Desktop.
ASP.NET fast upload file to AWS using multipart/form-data without take memory
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
var factories = context.ValueProviderFactories;
factories.RemoveType<FormValueProviderFactory>();
factories.RemoveType<JQueryFormValueProviderFactory>();
}
public void OnResourceExecuted(ResourceExecutedContext context)
{
}
}
<form action="upload" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<input type="submit" value="Upload" />
</form>
public class UploadController : Controller
{
[HttpPost]
[DisableFormValueModelBinding]
public async Task<IActionResult> Index()
{
if (!MediaTypeHeaderValue.TryParse(Request.ContentType, out var mt) ||
!mt.MediaType.Equals("multipart/form-data", StringComparison.OrdinalIgnoreCase))
{
return BadRequest();
}
var boundary = HeaderUtilities.RemoveQuotes(mt.Boundary);
var multipartReader = new MultipartReader(boundary.ToString(), Request.Body);
var section = await multipartReader.ReadNextSectionAsync(HttpContext.RequestAborted);
if (section == null)
{
return BadRequest($"No found any multipart section data.");
}
if (!ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition) ||
!contentDisposition.IsFileDisposition())
{
return BadRequest();
}
// Geting current position in request body.
var bufferOffsetield = typeof(BufferedReadStream).GetField("_bufferOffset", BindingFlags.NonPublic | BindingFlags.Instance);
var innerStreamField = section.Body.GetType().GetField("_innerStream", BindingFlags.NonPublic | BindingFlags.Instance);
var bufferedReadStream = innerStreamField.GetValue(section.Body);
var offset = (int)bufferOffsetield.GetValue(bufferedReadStream);
// the last line of boundary.
var discardLength = "\r\n--".Length + boundary.Length + "--\r\n".Length;
var fileLength = Request.ContentLength.Value - offset - discardLength;
// Upload file to AWS3 Using a Presigned URL feature.
// https://docs.aws.amazon.com/AmazonS3/latest/dev/UploadObjectPreSignedURLDotNetSDK.html
var endpointUrl = CreatePreSignedURL(contentDisposition.FileName.ToString());
var request = new HttpRequestMessage(HttpMethod.Put, endpointUrl);
request.Content = new StreamContent(section.Body);
request.Content.Headers.ContentLength = fileLength;
request.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
var response = await new HttpClient().SendAsync(request);
if (response.StatusCode != System.Net.HttpStatusCode.OK)
{
throw new AmazonS3Exception($"Upload file failed.{response.StatusCode}.");
}
return Ok();
}
private string CreatePreSignedURL(string key)
{
var request = new GetPreSignedUrlRequest()
{
BucketName = "BucketName",
Key = key,
Verb = HttpVerb.PUT,
Expires = DateTime.Now.AddMinutes(5),
};
return new AmazonS3Client().GetPreSignedURL(request);
}
}
@zhengchun
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment