On this page

Skip to content

Implementing Multiple File Uploads in ASP.NET Core MVC Using HTML5

TLDR

  • Use the HTML5 <input type="file" multiple> attribute to support multiple file selection.
  • The frontend should use the FormData object to encapsulate file data and listen for upload progress via Axios's onUploadProgress.
  • When receiving parameters in the backend Controller, use the IFormFileCollection type for model binding.
  • To adjust file upload size limits, use FormOptions for global configuration or attributes like [RequestSizeLimit] for individual Action adjustments.
  • Be sure to pay attention to the multipart/form-data Content-Type setting; when using FormData, the browser handles this automatically, and manual configuration is not recommended.

Frontend Implementation: FormData and Progress Listening

When to encounter this issue: When you need to upload multiple files asynchronously via AJAX and display a real-time upload progress bar.

In frontend implementation, the FormData object can be used to conveniently encapsulate multiple files into a Key/Value format. Through the Axios onUploadProgress event, you can obtain the loaded and total properties to calculate and update the progress bar value.

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 = {
                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);
            });
        }
    }
});

Backend Processing: IFormFileCollection Model Binding

When to encounter this issue: When the backend needs to receive multiple files sent from the frontend and wants to perform model binding directly via a ViewModel.

In ASP.NET Core, multiple file uploads should be handled using the IFormFileCollection type. This simplifies the tedious process of manually manipulating Request.Files as was required in MVC 5.

csharp
public class IndexViewModel {
    [DisplayName("Upload Files")]
    [Required]
    public IFormFileCollection Files { get; set; }
}

[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) {
            var filePath = Path.GetTempFileName();
            using var stream = System.IO.File.Create(filePath);
            await formFile.CopyToAsync(stream);
        }
    }
    return Ok(new { Message = "Upload successful" });
}

Adjusting File Upload Size Limits

When to encounter this issue: When uploaded files exceed the default limit (e.g., the default 128MB), causing the server to reject the request.

To adjust upload limits, you can choose between global configuration or configuration for a specific Action.

Global Configuration

Adjust the Kestrel or IIS request size limits in Program.cs:

csharp
builder.Services.Configure<FormOptions>(x => {
    x.MultipartBodyLengthLimit = long.MaxValue;
});

builder.WebHost.ConfigureKestrel(opt => {
    opt.Limits.MaxRequestBodySize = long.MaxValue;
});

builder.Services.Configure<IISServerOptions>(options => {
    options.MaxRequestBodySize = long.MaxValue;
});

Attribute Configuration

Adjust limits for a specific Action; this setting has a higher priority than the global configuration:

csharp
[HttpPost]
[DisableRequestSizeLimit]
[RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue)]
public async Task<IActionResult> Index(IndexViewModel viewModel) {
    // ...
}

DANGER

Notes

  • Setting long.MaxValue is for demonstration purposes only; please set the limit size according to your actual requirements.
  • The units for the settings above are in bytes.
  • Attribute priority is higher than global configuration.

Change Log

  • 2022-10-24 Initial documentation created.