Implementing Multi-file Upload in ASP.NET Core MVC Using HTML5
Versions Used
Prerequisites
HTML
FormData When using XMLHttpRequest, you can use FormData to send Key/Value data. It is primarily used for sending form data.
multipart/form-data When uploading files via a form, the "enctype" attribute of the form or the "Content-Type" in ajax headers must be set to this value. However, if you use FormData for ajax file uploads, the browser will add it automatically, so it is generally not necessary or recommended to set it manually.
HTML multiple Attribute A simple attribute. Using the
multipleattribute allows the input file to select multiple files. The standard way to write this ismultiple="multiple", but writingmultipleormultiple="{any value}"produces the same effect.
<input type="file" id="files" name="files" multiple="multiple" />Progress Event Handler XMLHttpRequest triggers this event periodically as more data is received. The Event Args have two properties that can be used to draw a Progress Bar.
- loaded: The amount of data loaded.
- total: The total amount of data to be loaded.
.NET
- IFormFile In the past, with MVC 5, file uploads could not be integrated with model binding properties, requiring the use of "HttpPostedFileBase" type parameters in the Action or "Request.Files" to retrieve uploaded file data. ASP.NET Core added the "IFormFile" type to serve as a binding property type for file uploads, and "IFormFileCollection" for multi-file uploads.
Practical Example
HTML
<div id="app">
<input type="file" multiple="multiple" asp-for="Files" v-on:change="handleFileChange" />
<div class="progress" v-show="progressBarValue > 0">
<div class="progress-bar" role="progressbar" :style="{ width: progressBarValue + '%' }" v-bind:aria-valuenow="{progressBarValue}" aria-valuemin="0" aria-valuemax="100">{{ progressBarValue }}%</div>
</div>
<button class="btn-primary" type="button" v-on:click="handleSubmit">Submit</button>
</div>JavaScript
new Vue({
el: '#app',
data: {
formData: new FormData,
progressBarValue: 0
},
methods: {
handleFileChange(e) {
this.formData = new FormData();
for (let i = 0; i < e.target.files.length; i++) {
this.formData.append(e.target.id, e.target.files[i]);
}
},
handleSubmit() {
let config = {
// Axios uses this as the XMLHttpRequest Progress Event
onUploadProgress: progressEvent => {
this.progressBarValue = (progressEvent.loaded / progressEvent.total * 100 | 0);
}
};
axios.post('@Url.Action("Index3")', this.formData, config).then(response => {
alert(response.data.message);
}).catch(thrown => {
alert(thrown);
});
}
}
});ViewModel
public class IndexViewModel {
[DisplayName("Upload Files")]
[Required]
public IFormFileCollection Files { get; set; }
}Controller
[HttpPost]
public async Task<IActionResult> Index3(IndexViewModel viewModel) {
if (!ModelState.IsValid) {
string message = ModelState.First(x => x.Value.Errors.Count > 0)
.Value?.Errors.FirstOrDefault()?.ErrorMessage;
return Ok(new { Message = message });
}
foreach (var formFile in viewModel.Files) {
if (formFile.Length > 0) {
// Please replace with the actual storage location
var filePath = Path.GetTempFileName();
using var stream = System.IO.File.Create(filePath);
await formFile.CopyToAsync(stream);
}
}
return Ok(new { Message = "Upload successful" });
}File Upload Size Limits
Due to security reasons, there are size limits for Requests, Responses, etc. When uploading files, two main properties are involved: MultipartBodyLengthLimit and MaxRequestBodySize. To adjust these limits from the code, you can use the following methods:
- Global Configuration.
// Program.cs
builder.Services.Configure<FormOptions>(x => {
// Maximum length limit for multipart body, default is about 128MB
x.MultipartBodyLengthLimit = long.MaxValue;
});
// Set Request size limit for applications deployed using Kestrel
builder.WebHost.ConfigureKestrel(opt => {
opt.Limits.MaxRequestBodySize = long.MaxValue;
});
// Set Request size limit for applications deployed using IIS
builder.Services.Configure<IISServerOptions>(options => {
options.MaxRequestBodySize = long.MaxValue;
});- Attribute-based limits.
[HttpPost]
[DisableRequestSizeLimit]
[RequestSizeLimit(long.MinValue)] // Use either this or DisableRequestSizeLimitAttribute
[RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue)]
public async Task<IActionResult> Index(IndexViewModel viewModel) {
//...
}DANGER
Notes
- Setting
long.MaxValueis for demonstration purposes only; please set the limit according to your actual requirements. - The units for the settings above are in bytes.
- Attributes have higher priority than global settings.
Change Log
- 2022-10-24 Initial version created.
