choose回调是唯一能做即时校验的入口,文件选中后仅在此处可获取原始file对象进行大小、类型、md5等同步校验,错过则无法获取文件数据。
choose 回调是唯一能做即时校验的入口
文件一旦被用户选中,layui 的 upload 组件只在 choose 回调里暴露原始 file 对象——这是你读取 size、name、type 甚至计算 md5 的**最后且唯一合法时机**。错过这个回调,后续的 before 里 obj 已不带文件数据,done 或 error 更是请求结束之后,校验完全失效。
常见错误是把校验逻辑写在 before 里,以为还能拿到文件信息;实际运行时 obj.getChooseFiles() 在某些版本中返回空数组或报错,导致校验逻辑静默跳过。
-
choose触发时机:用户点击“选择文件”→ 弹出系统对话框 → 用户确认后立即执行 - 此时
obj.files是标准FileList,单文件用obj.files[0],多文件需遍历 - IE 下部分旧版可能用
file.fileSize替代file.size,需兼容判断 - 校验失败必须手动调用
obj.reset(),否则 UI 显示“已选择”,但后续点击上传会无响应
大小和格式校验必须在 choose 里同步完成
文件大小超限、类型不符这类基础校验,必须在 choose 中同步判断并阻断,不能依赖服务端返回或异步提示。因为用户选完就期待立刻反馈,延迟到 before 甚至上传中再提示,体验断裂且浪费带宽。
示例中限制 5MB 且仅允许 PDF:
choose: function(obj) {
var file = obj.files[0];
if (!file) return;
<p>if (file.size > 5 <em> 1024 </em> 1024) {
layer.msg('文件不能大于 5MB');
obj.reset();
return;
}</p><p>if (file.type !== 'application/pdf' && !/.pdf$/i.test(file.name)) {
layer.msg('仅支持 PDF 格式');
obj.reset();
return;
}</p><p>obj.upload(); // 全部通过才放行
}</p>
- 注意
file.type不可靠(浏览器依赖 MIME 探测),务必结合file.name后缀双重校验 - 正则匹配后缀要用
/\.pdf$/i,避免误判my.pdf.bak这类名字 - 不要用
alert(),它阻塞主线程,且无法关闭;layer.msg()是 Layui 官方推荐方案 - 若配置了
auto: false,记得显式调用obj.upload(),否则不会上传
MD5 校验必须分片读取,且只能在 choose 中启动
防重复上传依赖文件内容哈希,而计算 MD5 必须读取二进制内容——这只能在 choose 中用 FileReader 分片完成。一次性读全量会卡死浏览器,尤其对百 MB 级文件。
关键点在于:SparkMD5.ArrayBuffer 的 end() 方法只能调用一次,且调用后内部状态清空。必须在全部分片加载完毕后立刻取值,然后挂载到 obj 上供后续使用:
choose: function(obj) {
var file = obj.files[0];
var spark = new SparkMD5.ArrayBuffer();
var chunkSize = 2 * 1024 * 1024;
var currentChunk = 0;
var chunks = Math.ceil(file.size / chunkSize);
var fileReader = new FileReader();
<p>fileReader.onload = function(e) {
spark.append(e.target.result);
currentChunk++;
if (currentChunk </p><p>function loadNext() {
var start = currentChunk * chunkSize;
var end = Math.min(start + chunkSize, file.size);
fileReader.readAsArrayBuffer(file.slice(start, end));
}</p><p>loadNext();
}</p>
- 分片大小建议 2MB,太大易内存溢出,太小则 I/O 开销高
- 不能在
before中尝试重新计算 MD5——此时file已不可访问 - 计算出的
obj.md5可在before中读取并拼入请求参数,例如obj.field.md5 = obj.md5 - 如果用户连续快速选择多个文件,需注意
obj实例是每次独立的,无需担心污染
多文件场景下校验失败不能简单 reset 全部
用户一次选 10 个文件,其中 2 个超限,你不能直接 obj.reset() 清空全部——这违背用户意图,也影响操作效率。正确做法是在 choose 中过滤合规文件,再逐个或批量上传。
需要改用 obj.upload() 的多文件模式,并传入过滤后的 FileList(注意:不是数组,需转为真实 FileList):
choose: function(obj) {
var validFiles = Array.from(obj.files).filter(function(file) {
return file.size if (validFiles.length === 0) {
layer.msg('所选文件均不符合要求(≤5MB,PDF 格式)');
obj.reset();
return;
}<p>if (validFiles.length !== obj.files.length) {
layer.msg('已自动过滤 ' + (obj.files.length - validFiles.length) + ' 个不合规文件');
}</p><p>// 手动触发上传,传入过滤后文件(需 polyfill FileList)
obj.upload({
files: validFiles
});
}</p>
- 原生
FileList不可构造,需用new DataTransfer().files或借助临时 input 模拟(Layui v2.8+ 支持直接传数组) - 若用旧版 Layui,更稳妥的方式是遍历
validFiles,对每个文件单独调用obj.upload({ files: [file] }) - 提示语要明确告知过滤数量,避免用户困惑“为什么少了几个”
- 服务端接口需适配多文件接收逻辑,不能假定每次只收一个
复杂点在于:校验逻辑一旦嵌入 choose,就和用户交互强耦合,任何异步操作(比如查重、权限验证)都会打断选择流程。所以真正需要服务端参与的校验,得提前做,或改用按钮触发式上传而非自动选择即传。











