Validating for large files upon Upload
One possibility is to write a custom validation attribute:
public class MaxFileSizeAttribute : ValidationAttribute
{
private readonly int _maxFileSize;
public MaxFileSizeAttribute(int maxFileSize)
{
_maxFileSize = maxFileSize;
}
public override bool IsValid(object value)
{
var file = value as HttpPostedFileBase;
if (file == null)
{
return false;
}
return file.ContentLength <= _maxFileSize;
}
public override string FormatErrorMessage(string name)
{
return base.FormatErrorMessage(_maxFileSize.ToString());
}
}
and then you could have a view model:
public class MyViewModel
{
[Required]
[MaxFileSize(8 * 1024 * 1024, ErrorMessage = "Maximum allowed file size is {0} bytes")]
public HttpPostedFileBase File { get; set; }
}
controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View(new MyViewModel());
}
[HttpPost]
public ActionResult Index(MyViewModel model)
{
if (!ModelState.IsValid)
{
// validation failed => redisplay the view
return View(model);
}
// the model is valid => we could process the file here
var fileName = Path.GetFileName(model.File.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
model.File.SaveAs(path);
return RedirectToAction("Success");
}
}
and a view:
@model MyViewModel
@using (Html.BeginForm(null, null, FormMethod.Post, new { enctype = "multipart/form-data" }))
{
@Html.TextBoxFor(x => x.File, new { type = "file" })
@Html.ValidationMessageFor(x => x.File)
<button type="submit">OK</button>
}
Now of course for this to work you will have to increase the maximum allowed upload file size in web.config to a sufficiently large value:
<!-- 1GB (the value is in KB) -->
<httpRuntime maxRequestLength="1048576" />
and for IIS7:
<system.webServer>
<security>
<requestFiltering>
<!-- 1GB (the value is in Bytes) -->
<requestLimits maxAllowedContentLength="1073741824" />
</requestFiltering>
</security>
</system.webServer>
We could now bring our custom validation attribute a step further and enable client side validation to avoid wasting bandwidth. Of course verifying the file size before uploading is only possible with HTML5 File API. As a consequence only browsers that support this API will be able to take advantage of it.
So the first step is to make our custom validation attribute implement the IClientValidatable interface which will allow us to attach a custom adapter in javascript:
public class MaxFileSizeAttribute : ValidationAttribute, IClientValidatable
{
private readonly int _maxFileSize;
public MaxFileSizeAttribute(int maxFileSize)
{
_maxFileSize = maxFileSize;
}
public override bool IsValid(object value)
{
var file = value as HttpPostedFileBase;
if (file == null)
{
return false;
}
return file.ContentLength <= _maxFileSize;
}
public override string FormatErrorMessage(string name)
{
return base.FormatErrorMessage(_maxFileSize.ToString());
}
public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
{
var rule = new ModelClientValidationRule
{
ErrorMessage = FormatErrorMessage(_maxFileSize.ToString()),
ValidationType = "filesize"
};
rule.ValidationParameters["maxsize"] = _maxFileSize;
yield return rule;
}
}
and all that's left is configure the custom adapter:
jQuery.validator.unobtrusive.adapters.add(
'filesize', [ 'maxsize' ], function (options) {
options.rules['filesize'] = options.params;
if (options.message) {
options.messages['filesize'] = options.message;
}
}
);
jQuery.validator.addMethod('filesize', function (value, element, params) {
if (element.files.length < 1) {
// No files selected
return true;
}
if (!element.files || !element.files[0].size) {
// This browser doesn't support the HTML5 API
return true;
}
return element.files[0].size < params.maxsize;
}, '');
Validate file's extension & size before uploading
The validation rules and and request data validation against it happens on the server once the Request data is received.
So naturally even if you try to upload 2GB file or a zip file, it will have to reach the server before it gets validated against the validation rules.
You must also implement some validation in the frontend to prevent such issues.
Say for example you can check the mime type and size of the file being uploaded on the frontend (via javascript) and only if it passes the validation here on frontend allow request to be made to the server with the uploaded file.
However never rely only on validation on the frontend. Validation at server level is must.
For example to validate file being uploaded does not exceed 20MB in size
function isValidSize(file) {
const errors = [];
const maxUploadSizeInBytes = 20 * 1024 * 1024;
let valid = true;
if (file.size > maxUploadSizeInBytes) {
valid = false;
let sizeInKb = maxUploadSizeInBytes / 1024;
let sizeForHumans = sizeInKb < 1024
? `${sizeInKb} KB`
: `${sizeInKb / 1024} MB`;
this.errors.push(
`The file exceeds the maximum allowed size of ${sizeForHumans}`
);
}
return valid;
}
Function to validate mime type of the file being uploaded against allowed mime types
isValidType(file) {
const errors = [];
let acceptedMimes = "jpg,jpeg,png,webp"
.trim()
.split(",")
.map(type => `image/${type}`);
let valid = true;
if (!acceptedMimes.includes(file.type)) {
valid = false;
let phrase = acceptedMimes.replace(/,/g, " or ");
this.errors.push(
`The file type is not allowed. Please upload ${phrase} file.`
);
}
return valid;
}
Validation on Server (backend) is a MUST
Frontend validation is for better user experience and to save some unnecessary network requestsHow to validate uploaded file in ASP.Net Core
You could custom validation attribute MaxFileSizeAttribute
like below
MaxFileSizeAttribute
public class MaxFileSizeAttribute : ValidationAttribute
{
private readonly int _maxFileSize;
public MaxFileSizeAttribute(int maxFileSize)
{
_maxFileSize = maxFileSize;
}
protected override ValidationResult IsValid(
object value, ValidationContext validationContext)
{
var file = value as IFormFile;
if (file != null)
{
if (file.Length > _maxFileSize)
{
return new ValidationResult(GetErrorMessage());
}
}
return ValidationResult.Success;
}
public string GetErrorMessage()
{
return $"Maximum allowed file size is { _maxFileSize} bytes.";
}
}
AllowedExtensionsAttribute
public class AllowedExtensionsAttribute : ValidationAttribute
{
private readonly string[] _extensions;
public AllowedExtensionsAttribute(string[] extensions)
{
_extensions = extensions;
}
protected override ValidationResult IsValid(
object value, ValidationContext validationContext)
{
var file = value as IFormFile;
if (file != null)
{
var extension = Path.GetExtension(file.FileName);
if (!_extensions.Contains(extension.ToLower()))
{
return new ValidationResult(GetErrorMessage());
}
}
return ValidationResult.Success;
}
public string GetErrorMessage()
{
return $"This photo extension is not allowed!";
}
}
Add MaxFileSize
attribute and AllowedExtensions
attribute to Photo
property
public class UserViewModel
{
[Required(ErrorMessage = "Please select a file.")]
[DataType(DataType.Upload)]
[MaxFileSize(5* 1024 * 1024)]
[AllowedExtensions(new string[] { ".jpg", ".png" })]
public IFormFile Photo { get; set; }
}
JavaScript file upload size validation
Yes, you can use the File API for this.
Here's a complete example (see comments):
document.getElementById("btnLoad").addEventListener("click", function showFileSize() {
// (Can't use `typeof FileReader === "function"` because apparently it
// comes back as "object" on some browsers. So just see if it's there
// at all.)
if (!window.FileReader) { // This is VERY unlikely, browser support is near-universal
console.log("The file API isn't supported on this browser yet.");
return;
}
var input = document.getElementById('fileinput');
if (!input.files) { // This is VERY unlikely, browser support is near-universal
console.error("This browser doesn't seem to support the `files` property of file inputs.");
} else if (!input.files[0]) {
addPara("Please select a file before clicking 'Load'");
} else {
var file = input.files[0];
addPara("File " + file.name + " is " + file.size + " bytes in size");
}
});
function addPara(text) {
var p = document.createElement("p");
p.textContent = text;
document.body.appendChild(p);
}
body {
font-family: sans-serif;
}
<form action='#' onsubmit="return false;">
<input type='file' id='fileinput'>
<input type='button' id='btnLoad' value='Load'>
</form>
How to validate uploaded files by FluentValidation
your ViewModel
must have public IList<IFormFile> Files { get; set; }
:
public class CustomViewModel
{
public IList<IFormFile> Files { get; set; }
...
}
you must create a validator for IFormFile
type as below:
public class FileValidator : AbstractValidator<IFormFile>
{
public FileValidator()
{
RuleFor(x => x.Length).NotNull().LessThanOrEqualTo(100)
.WithMessage("File size is larger than allowed");
RuleFor(x => x.ContentType).NotNull().Must(x => x.Equals("image/jpeg") || x.Equals("image/jpg") || x.Equals("image/png"))
.WithMessage("File type is larger than allowed");
...
}
}
now you can use FileValidator
in your CustomValidator
like this:
public class CustomValidator : AbstractValidator<CustomViewModel>
{
public CustomValidator()
{
RuleForEach(x => x.Files).SetValidator(new FileValidator());
}
}
Validate upload size
Have you already read official msdn documentation?
The method Length():
gets the length in bytes of the stream.
Actually, 51200 bytes are equals to 0,05 Mb.
Have you tried to change it?
If you need to block upload with more than 50mb of size, you need to set it to:
50 Mb = 52,428,800 bytes.
Struts2: Specify max file size in upload validation message
If you can then upgrade to latest S2 version where this feature is already implemented. See jira issue WW-4389 for more details.
Another option (if you cannot upgrade) is to extend FileUploadInterceptor
class and override acceptFile
method where this error message is being created.
Laravel - validate file size when PHP max upload size limit is exceeded
You don't seem interested in changing the PHP limits to allow larger files. It looks to me like you want your max upload to be 5MB, and return a proper response if it is over that.
You can handle the FileException
exception inside your exception handler at app/Exceptions/Handler.php
. Update the render
method to add in the code you need. For example, if you'd like to return a validation exception, you will need to handle the validation inside the exception handler for the FileException
exception.
public function render($request, Exception $exception)
{
if ($exception instanceof \Symfony\Component\HttpFoundation\File\Exception\FileException) {
// create a validator and validate to throw a new ValidationException
return Validator::make($request->all(), [
'your_file_input' => 'required|file|size:5000',
])->validate();
}
return parent::render($request, $exception);
}
This is untested, but should give you the general idea.
You can also do client side validation via javascript, so that a file that is too large is never actually sent to your server, but javascript can be disabled or removed by the client, so it would be good to have nice server side handling set up.
For the client side validation, if you attach an event handler to the "change" event for the file input, you can check the file size using this.files[0].size
, and perform any additional actions after that (disable form, remove uploaded file, etc.)
Related Topics
How to Ensure a Timestamp Is Always Unique
When Is Readerwriterlockslim Better Than a Simple Lock
Access Form Component from Another Class
Mvvm: Binding to Model While Keeping Model in Sync with a Server Version
What's the Role of Gethashcode in the Iequalitycomparer<T> in .Net
How to Extend C# Built-In Types, Like String
Why Method Overloading Is Not Allowed in Wcf
What Is Myassembly.Xmlserializers.Dll Generated For
How to Split a String into Multiple Values
Translate Perl Regular Expressions to .Net
No Definition Found for Getactiveobject from System.Runtime.Interopservices.Marshal C#
How to Create a New Language for Use in Visual Studio
Why Does Computing Factorial of Relatively Small Numbers (34+) Return 0
Customattribute Reflects HTML Attribute MVC5
Simple Delegate (Delegate) VS. Multicast Delegates
Cannot Load Counter Name Data Because an Invalid Index -Exception
Linq to Entities Does Not Recognize the Method 'System.Web.Mvc.Fileresult'