Skip to content
View Article Network

Implementing Multi-file Upload in ASP.NET Core MVC Using HTML5

Versions Used

Prerequisites

HTML

  • FormData When using XMLHttpRequest, you can use FormData to set Key/Value data for transmission. 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 the 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 multiple attribute allows the input file to select multiple files. The standard way to write this is multiple="multiple", but writing multiple or multiple="{any value}" has the same effect.

html
<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 contain two properties that can be used to draw a Progress Bar.

    • loaded: The amount of data already 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 the binding property type for file uploads, and "IFormFileCollection" for multi-file uploads.

Practical Example

HTML

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

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

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

Controller

csharp
[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, Request and Response objects have size limits. When uploading files, the two main properties involved are MultipartBodyLengthLimit and MaxRequestBodySize. To adjust these limits from the code, you can use the following methods:

  • Global configuration.
csharp
// 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 restriction.
csharp
[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.MaxValue is for demonstration purposes only; please set the limit 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 document created.