检查所选文件是否匹配<input />标记上的accept属性

时间:2013-12-11 16:15:21

标签: javascript html html5 validation

我希望阻止用户上传一个文件,服务器将从一个包含最少JavaScript的页面中拒绝该文件,理想情况下,不会添加任何重度依赖项,如jQuery,只能解决这个问题。

由于我不是针对传统用户,因此我使用浏览器的表单验证系统检查用户是否选择了有效文件,但是它似乎只关心用户是否选择了一个文件而不管其类型。

> i = document.querySelector('input[type=file]')
<input type=​"file" accept=​"image/​*" name=​"attachment" required>​
> i.accept
"image/*"
> i.files[0].type
"application/x-zip-compressed"
> i.checkValidity()
true

有一种简单的方法吗?我发现唯一接近的是jQuery Validate,但这是一个重量级的解决方案。

4 个答案:

答案 0 :(得分:4)

您可以执行RegExp测试 - 以下内容转换MIME类型字符串中的通配符以匹配RegExp语法,并针对输入文件的类型进行测试:

( new RegExp( i.accept.replace( '*', '.\*' ) ) ).test( i.files[ 0 ].type )

Demo here

编辑:

我最终找到了一种方法,可以使本机浏览器验证行为无缝地实现此功能(即阻止提交无效输入,使用本机验证警告通知用户),但我不确定代码是如何工作的,或者这是不错的做法(I've asked about the stranger parts here)。但是,这似乎表现得如预期,至少在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(尝试使用无效的文件类型提交)。

答案 1 :(得分:1)

仅当accept为单个值时,可接受的答案才有效。另外,它不支持multiple属性。对于多个接受值,逗号分隔和多个文件,请使用以下命令:

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

提琴:http://jsfiddle.net/ynj8dsu6/

答案 2 :(得分:1)

这是从其他文章中收集的技术,并使其成为简短易用的功能,可以使用多个表达式:

verifyAccept = function( file-type, accept ) {
    var type-regex = new RegExp( accept.replace( /\*/g, '.\*' ).replace( /\,/g, '|' ) );
    return type-regex.test( file-type );
}

它使用正则表达式功能,而不是遍历拆分后的接受字符串,只需通过 | 替换即可,这意味着可以对单个表达式进行运算。

答案 3 :(得分:0)

不需要像其他解决方案提供的任何复杂的正则表达式。

/**
 * Check if a mime type matches the set given in accept
 *
 * @param type the mime type to test, ex image/png
 * @param accept the mime types to accept, ex audio/*,video/*,image/png
 * @returns true if the mime is accepted, false otherwise
 */
function verifyAccept(type: string, accept: string): boolean {
  const allowed = accept.split(',').map(x => x.trim());
  return allowed.includes(type) || allowed.includes(type.split('/')[0] + '/*');
}