I wish to prevent the user from uploading a file the server will reject from a page with minimal JavaScript on it, ideally without adding any heavy dependencies like jQuery
The accepted answer only works when accept
is a single value. Also, it doesn't support the multiple
attribute. For multiple accept values, comma separated, and multiple files, use the following:
window.validateFileFormat = function() {
const valid = [...i.files].every(file => {
if (!i.accept) {
return true;
}
return i.accept.replace(/\s/g, '').split(',').filter(accept => {
return new RegExp(accept.replace('*', '.*')).test(file.type);
}).length > 0;
});
alert('Valid: ' + valid);
}
Fiddle: http://jsfiddle.net/ynj8dsu6/
You could just perform a RegExp test — the following converts the wildcard in MIME type strings to match RegExp syntax, and tests that against the input file's type:
( new RegExp( i.accept.replace( '*', '.\*' ) ) ).test( i.files[ 0 ].type )
Demo here.
EDIT:I eventually found a way to make this functionality seamless with native browser validation behaviour (ie prevent submission for invalid inputs, notify user using native validation warnings), but I'm not exactly sure how the code works or whether it's good practice (I've asked about the stranger parts here). However, this appears to behave as expected, at least in Chrome 31:
void function enhanceFileInputTypeValidityCheck(){
var inputPrototype = document.createElement( 'input' ).constructor.prototype;
var nativeCheckValidity = inputPrototype.checkValidity;
function validateFileInputType( input ){
var MIMEtype = new RegExp( input.accept.replace( '*', '.\*' ) );
return Array.prototype.every.call( input.files, function passesAcceptedFormat( file ){
return MIMEtype.test( file.type );
} );
}
function validateInputs(){
Array.prototype.forEach.call( document.querySelectorAll( 'input, select' ), function callValidation( input ){
input.checkValidity();
} );
}
inputPrototype.checkValidity = function enhancedCheckValidity(){
if( this.type === 'file' && this.accept && this.files && this.files.length ){
if( !validateFileInputType( this ) ){
this.setCustomValidity( 'Please only submit files of type ' + this.accept );
return false;
}
}
return nativeCheckValidity.apply( this );
}
Array.prototype.forEach.call( [ 'change', 'input' ], function bindValidation( event ){
document.documentElement.addEventListener( event, validateInputs );
} );
}();
Demo here (attempt to submit with an invalid file type).
This one gathers the techniques from the other posts and make it to a short and easy function, working with multiple expressions:
verifyAccept = function( file-type, accept ) {
var type-regex = new RegExp( accept.replace( /\*/g, '.\*' ).replace( /\,/g, '|' ) );
return type-regex.test( file-type );
}
instead of looping through the splitted accept string it uses regular expression features, just replace , through | which means the single expressions are or'ed.