Angular/TypeScript读取CSV报readAsText参数非Blob错误如何修复
问题根因
这个报错是TypeScript类型擦除+错误强转导致的运行时类型不匹配:
- 你写的
CustomFile extends Blob只是TS编译阶段的接口约束,不会生成任何运行时代码,也不会让符合接口结构的普通对象自动继承Blob的原型链,FileReader.readAsText要求传入的参数必须是原生Blob/File实例(原型链上包含Blob),光属性结构匹配无法被API识别。 - 代码里的
as Blob只是TS编译阶段的类型断言,不会对实际的JS对象做任何转换,相当于骗过了TS的类型检查,但运行时传进去的如果是普通包装对象、没有挂载Blob原型,就会直接抛错。 - 大概率你从上传组件拿到的
files数组里的元素,是组件库包装过的自定义对象,不是原生File实例,这类对象一般会把真实的原生File存在特定属性上。
修复步骤
1. 修正类型导入和函数标注,避免类型混淆
先明确区分原生File和自定义File类型,不要在同一个作用域混用同名类型:
// 自定义类型重命名导入,避免和浏览器原生File冲突 import { File as CustomFile } from '@myLibrary/cs-tools'; import * as lodash from 'lodash'; // 函数参数明确标注为CustomFile类型,不要用原生File onUploadFileChanges(files: CustomFile[]) { if (!lodash.isEmpty(files)) { // 去掉无意义的as Blob强转,先拿到实际对象 const uploadedFile = lodash.last(files); if (!uploadedFile) { // 修正原代码的拼写错误:fuond -> found throw new Error('uploaded file not found'); }
2. 运行时校验并获取真实Blob实例
永远不要依赖TS类型断言判断运行时类型,手动校验对象是不是真的Blob实例:
let targetBlob: Blob; // 先判断拿到的对象本身是不是原生Blob实例 if (uploadedFile instanceof Blob) { targetBlob = uploadedFile; } else { // 如果是组件包装的对象,优先取内置的原生File属性 // 常见属性名:originFileObj、rawFile、nativeFile,根据你用的上传组件实际字段调整 // 例:如果是ng-zorro上传,直接取uploadedFile.originFileObj即可 const nativeFile = (uploadedFile as any).originFileObj || (uploadedFile as any).rawFile; if (nativeFile instanceof Blob) { targetBlob = nativeFile; } else { // 如果没有内置原生文件,就根据文件内容构造Blob targetBlob = new Blob([/* 填入实际的文件二进制内容 */], { type: uploadedFile.type }); } } const reader: FileReader = new FileReader(); // 传入经过校验的真实Blob实例 reader.readAsText(targetBlob);
3. 保留原有CSV解析逻辑,可补充小优化
后续解析逻辑不需要大改,建议加个空行过滤,避免CSV首尾空行生成无效数据:
reader.onload = () => { const csv = reader.result as string; const rowStrings = csv.split(/\r\n|\n|\r/); // 优化正则,优先匹配Windows的\r\n换行 let headers = rowStrings.shift()?.split(CSV_DELIMITER); const rows: Array<Array<string>> = []; rowStrings.forEach((row: string) => { // 跳过空行 if (row.trim()) { rows.push(row.split(CSV_DELIMITER)); } }); headers = headers?.map((header) => header.trim()); console.log('rows : ', rows); console.log('headers : ', headers); if (headers?.length) { rows = lodash.filter(rows, (row) => row.length > headers.length / 2); } }; } }
注意事项
- TS的
interface/type只是编译时的类型约束,没有任何运行时效力,不要用as强转代替运行时类型校验,尤其是处理浏览器原生API、组件库返回值这类跨边界的场景。 - 如果不确定自定义文件对象的结构,可以先打个断点查看
uploadedFile的属性,找到存原生File的字段直接用即可,不需要自己手动构造Blob。
内容的提问来源于stack exchange,提问作者Fahad Subzwari
相关产品推荐
相关产品推荐

