FileReader读取file必须在choose回调中操作,因only此处能同步获取原始File实例;其他回调已丢失二进制上下文;需判空files、用readAsArrayBuffer或readAsDataURL读取;ArrayBuffer须转Blob再append至FormData上传;分片需手动slice并逐片读取;必须设auto: false避免流程冲突。
choose回调里用FileReader读取file对象
必须在choose回调中操作,这是唯一能同步拿到原始file实例的地方。其他回调如before、done都已丢失文件二进制上下文——before里的obj不带files,done只返回服务器响应。
- 取第一个文件:
const file = obj.files[0],注意判空,避免用户点取消后files为空 - 用
FileReader读取:const reader = new FileReader(),监听reader.onload获取reader.result(base64或arraybuffer) - 若需二进制流而非base64,调用
reader.readAsArrayBuffer(file);若要base64用于图片预览,用reader.readAsDataURL(file) - 不要在
preview回调里读取——它只对图片生效,且部分安卓WebView下静默失败
上传前把ArrayBuffer塞进FormData发出去
拿到ArrayBuffer后不能直接丢给XMLHttpRequest,得包装成Blob再append进FormData,否则后端收不到原始二进制。
- 转Blob:
const blob = new Blob([arrayBuffer], { type: file.type }) - 构造FormData:
const fd = new FormData(),然后fd.append('file', blob, file.name) - 手动发起请求:
fetch(url, { method: 'POST', body: fd })或$.ajax({ url, type: 'POST', data: fd, processData: false, contentType: false }) - 别用
upload.upload()——它只支持整个File对象上传,不接受已读取的ArrayBuffer
大文件分片时每片都走同样的FileReader流程
分片不是靠配置,而是靠你在choose里手动切:file.slice(start, end),每片都是独立Blob,需单独读取为ArrayBuffer再发。
- 切片大小建议2–5MB:
const chunkSize = 2 * 1024 * 1024,太大易超时,太小HTTP开销高 - 每次切片后都要新建
FileReader实例,不能复用——readAsArrayBuffer是单次操作 - 注意
slice()兼容性:IE10+才支持,旧版需降级用webkitSlice或mozSlice - 最后一片要用
Math.min(start + chunkSize, file.size)防越界,否则slice返回空Blob
别碰upload自带的auto上传逻辑
一旦你启用了auto: true或没设bindAction,Layui会自动触发完整文件上传,此时你读到的ArrayBuffer和后续请求就完全脱节了——两套流程并行,数据对不上。
- 必须设
auto: false,并显式调用obj.upload()仅当你自己完成所有校验和预处理之后 - 如果只是想传二进制流,根本不用调
obj.upload(),直接用自己的fetch或ajax发就行 - 混用
upload.render和手动FormData时,确保DOM里没有被Layui隐藏的原生input[type=file]干扰读取
真正难的是内存控制和错误边界:频繁读大文件时FileReader容易卡顿,ArrayBuffer不释放会堆积内存,而slice()出错时没有明确报错机制——这些细节不在文档里,但线上出问题时全靠它们撑住。


















