使用FileReader时如何避免出现NotReadable错误?
错误诱发原因
- 临时文件访问权限过期:该报错是典型的文件引用获取后权限丢失场景,最常出现在移动设备上:安卓10+的分区存储机制限制了Web对本地文件的临时访问时长,若获取文件引用后没有立即读取,系统会自动回收权限;部分定制ROM、微信/抖音等内置WebView也会主动缩短临时文件的有效时长,另外选择云盘、外部SD卡中的未缓存文件时,也会出现权限获取失败的问题。
- 文件引用被提前释放:如果业务逻辑中存在
input.value = ''的重置操作(用于支持重复选择同一个文件),若重置操作放在FileReader读取完成之前执行,会直接销毁已获取的文件引用,导致读取失败。 - 文件本身异常:选中的文件是0字节空文件、加密文件、正在被其他进程占用的文件、或者已损坏的文件时,也会触发该错误。
- 浏览器兼容问题:部分旧版本移动浏览器对FileReader的大文件读取有阈值限制,若选中的文件体积超过浏览器限制(通常是500M以上),也会抛出读取异常。
修复方案
- 优先同步读取文件:获取到
files[0]后立即执行readAsDataURL操作,不要在change事件触发后、读取前插入任何异步逻辑(比如接口请求、用户二次确认弹框等),避免权限过期。 - 调整input重置时机:如果需要支持重复选择同一文件,将
input.value = ''的操作放到FileReader的onload或onerror回调执行完成之后,不要提前销毁文件引用。 - 增加前置校验逻辑:读取前先判断文件的
size属性是否大于0,若为0字节直接提示用户重新选择文件,避免无效读取。 - 异常兜底处理:捕获到NotReadable错误时,直接提示用户重新选择文件,同时清空input的值,避免后续选择同个文件仍然报错。
- WebView场景适配:如果代码运行在原生APP内嵌的WebView中,需要让原生侧提前申请本地存储读取权限,安卓端可在AndroidManifest中开启
android:requestLegacyExternalStorage="true"配置,兼容分区存储限制。
优化后代码示例
$(document).on("change", "input[type='file']", function (event) { const input = event.target; const files = input.files; if (!files || !files[0]) return; const selectedFile = files[0]; // 前置校验文件有效性 if (selectedFile.size === 0) { alert("请选择有效文件,不支持0字节文件"); input.value = ''; return; } const reader = new FileReader(); reader.onload = () => { $(input) .closest(".row") .find(".attachment-data") .val(reader.result.split(",")[1]); // 读取完成后再重置input,支持重复选同个文件 input.value = ''; }; reader.onerror = (error) => { console.log("reader.onerror", error); alert("文件读取失败,请重新选择文件"); // 读取失败也重置input input.value = ''; }; // 拿到文件后立即同步读取,不插入任何异步操作 reader.readAsDataURL(selectedFile); });
内容的提问来源于stack exchange,提问作者ShafiVayyattukavil
相关产品推荐
相关产品推荐

