Validating for Large Files Upon Upload

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 requests

How 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



Leave a reply



Submit