Skip to content

Instantly share code, notes, and snippets.

@Legends
Last active October 28, 2019 11:28
Show Gist options
  • Select an option

  • Save Legends/5f458941913a7546f16adc5756e8a9a6 to your computer and use it in GitHub Desktop.

Select an option

Save Legends/5f458941913a7546f16adc5756e8a9a6 to your computer and use it in GitHub Desktop.
// UploadController Action:
[HttpPost]
[DisableFormValueModelBinding]
[ValidateAntiForgeryToken]
[DisableRequestSizeLimit]
public async Task<IActionResult> UploadPhysicalStreamed()
{
if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType))
{
ModelState.AddModelError("File", $"The request couldn't be processed (Error 1).");
// Log error
return BadRequest(ModelState);
}
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)
{
var hasContentDispositionHeader =
ContentDispositionHeaderValue.TryParse(section.ContentDisposition, out var contentDisposition);
if (hasContentDispositionHeader)
{
// This check assumes that there's a file
// present without form data. If form data
// is present, this method immediately fails
// and returns the model error.
if (!MultipartRequestHelper.HasFileContentDisposition(contentDisposition))
{
ModelState.AddModelError("File", $"The request couldn't be processed (Error 2).");
// Log error
return BadRequest(ModelState);
}
else
{
// Don't trust the file name sent by the client. To display
// the file name, HTML-encode the value.
var trustedFileNameForDisplay = WebUtility.HtmlEncode(contentDisposition.FileName.Value);
var trustedFileNameForFileStorage = Path.GetRandomFileName();
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
using (var targetStream = System.IO.File.Create(Path.Combine(_targetFilePath, trustedFileNameForFileStorage)))
{
//await targetStream.WriteAsync(streamedFileContent);
await section.Body.CopyToAsync(targetStream);
_logger.LogInformation(
"Uploaded file '{TrustedFileNameForDisplay}' saved to " +
"'{TargetFilePath}' as {TrustedFileNameForFileStorage}",
trustedFileNameForDisplay, _targetFilePath,
trustedFileNameForFileStorage);
}
}
}
// Drain any remaining section body that hasn't been consumed and
// read the headers for the next section.
section = await reader.ReadNextSectionAsync();
}// end while loop
return Created(nameof(StreamingController), null);
}
// Resource FIlter Attribute
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class DisableFormValueModelBindingAttribute : Attribute, IResourceFilter
{
public void OnResourceExecuting(ResourceExecutingContext context)
{
var factories = context.ValueProviderFactories;
factories.RemoveType<FormValueProviderFactory>();
factories.RemoveType<FormFileValueProviderFactory>();
factories.RemoveType<JQueryFormValueProviderFactory>();
}
public void OnResourceExecuted(ResourceExecutedContext context)
{
}
}
// Upload-Ajax-View.cshtml -without progress
@page
@model StreamedSingleFileUploadPhysicalModel
@{
ViewData["Title"] = "Streamed Single File Upload with AJAX (Physical)";
}
<h1>Stream a file with AJAX to physical storage with a controller endpoint</h1>
<p>The following form's <code>action</code> points to a controller endpoint that only recieves the file and saves it to disk. If additional form data is added to the form, the additional data is ignored by the controller action.</p>
<form id="uploadForm" action="Streaming/UploadPhysical" method="post"
enctype="multipart/form-data" onsubmit="AJAXSubmit(this);return false;">
<dl>
<dt>
<label for="file">File</label>
</dt>
<dd>
<input id="file" type="file" name="file" />
</dd>
</dl>
<input class="btn" type="submit" value="Upload" />
<div style="margin-top:15px">
<output form="uploadForm" name="result"></output>
</div>
</form>
@section Scripts {
<script>
"use strict";
async function AJAXSubmit (oFormElement) {
const formData = new FormData(oFormElement);
try {
const response = await fetch(oFormElement.action, {
method: 'POST',
headers: {
'RequestVerificationToken': getCookie('RequestVerificationToken')
},
body: formData
});
oFormElement.elements.namedItem("result").value = 'Result: ' + response.status + ' ' + response.statusText;
} catch (error) {
console.error('Error:', error);
}
}
function getCookie(name) {
var value = "; " + document.cookie;
var parts = value.split("; " + name + "=");
if (parts.length == 2) return parts.pop().split(";").shift();
}
</script>
}
// Upload script updated to show upload progress (upload progress currently not supported on fetch, but download progress is!)
@section Scripts {
<script>
"use strict";
function AJAXSubmit(thisForm) {
const formData = new FormData(thisForm);
var xhr = new XMLHttpRequest();
xhr.onload = function (e) {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
console.log(xhr.responseText);
} else {
console.error(xhr.statusText);
}
}
};
xhr.upload.addEventListener("progress", updateProgress);
xhr.onerror = function (e) {
console.error(xhr.statusText);
};
xhr.open("POST", thisForm.action, true);
xhr.setRequestHeader("RequestVerificationToken", getCookie('RequestVerificationToken'));
xhr.send(formData);
}
// progress on transfers from the server to the client (downloads)
function updateProgress(oEvent) {
if (oEvent.lengthComputable) {
var percentComplete = oEvent.loaded / oEvent.total * 100;
console.log("percentComplete: " + percentComplete);
// ...
} else {
// Unable to compute progress information since the total size is unknown
console.log("Unable to compute progress information since the total size is unknown");
}
}
function getCookie(name) {
var value = "; " + document.cookie;
var parts = value.split("; " + name + "=");
if (parts.length == 2) return parts.pop().split(";").shift();
}
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment