Layui旧版本before回调需返回false或Promise.reject()才生效,v2.6.8+已修复;多文件需Array.from转换FileList;异步校验必须返回Promise并reject拦截;exts仅限客户端过滤,真实校验须在before中实现。
before 回调必须返回 false 或 Promise.reject() 才能真正拦截
很多人写 before 函数时只做校验、弹提示,却没阻断流程,结果文件照传。根本原因是:layui 旧版本(v2.6.7 之前)对 return false 的处理有缺陷——它不检查返回值,直接往下走。所以光写 return false 没用。
解决方法分两种:
- 升级到 Layui v2.6.8+,官方已修复该问题,
before中直接return false即可生效 - 若无法升级,需手动修改
upload.js中的y = function()块,把原逻辑替换成:return (l.before && l.before(g)) === false ? '' : a.ie ? a.ie > 9 ? u() : c() : void u(); - 注意:修改后务必清空浏览器缓存,否则仍加载旧 JS
多文件场景下,obj.getChooseFiles() 返回的是 FileList,不是数组
obj.getChooseFiles() 看似返回数组,实则是只读的 FileList 对象。直接用 files.map 或 files.forEach 会报错。
正确做法是先转成真数组:
var files = Array.from(obj.getChooseFiles()); // 或 var files = [].slice.call(obj.getChooseFiles());
常见误操作:
- 用
files[0].size判断单个文件大小 → 多选时漏检其余文件 - 在
preview回调里return false→ 这个return只作用于当前预览循环,不影响上传主流程 - 校验逻辑写在
preview里而非before→ 完全无效
需要异步校验(如权限/服务端规则)时,必须返回 Promise
比如要先查用户角色是否允许上传 .zip,或校验文件哈希是否在白名单中,这类操作必须异步。此时 before 必须返回 Promise,且仅当 Promise 被 reject 时才拦截上传。
错误写法:return axios.get(...).then(...).catch(() => false) → 这个 false 是 Promise 的 resolve 值,不触发拦截
正确写法:
before: function(obj) {
return new Promise(function(resolve, reject) {
$.get('/api/upload/check', { type: 'zip' }, function(res) {
if (res.ok) resolve();
else reject(); // 或 reject('no permission')
});
});
}
关键点:
- Promise resolve → 继续上传;reject → 拦截并触发
error回调 - 不能混用同步返回(
return false)和异步 Promise,否则行为不可控 - Layui 不会自动等待 Promise,必须显式返回,否则视为同步通过
exts 和 acceptMime 是客户端过滤,不能替代 before 校验
exts: 'pdf|docx' 和 acceptMime: 'application/pdf' 只控制文件选择框的可选范围,用户仍可通过拖拽、重命名绕过。真实拦截必须落在 before 里。
例如:把 test.jpg 改名为 test.pdf,exts 无法识别,但 before 中用 file.type 或二进制头校验就能捕获。
建议组合使用:
-
exts提升体验,减少无效选择 -
before中用file.name.match(/\.[^\.]+$/i)取后缀再比对,防伪名 - 高安全要求时,用
FileReader读取前几百字节校验 magic number
最易被忽略的一点:before 函数里改 this.data 可以动态透传参数到后端,但若在拦截逻辑中修改了 this.data 却又 return false,这些数据不会发送——这点常被当成 bug 报告,其实是设计如此。











