file 是 blob 的子类,额外拥有 name、lastmodified 等元信息;上传时 file 自动携带 filename,blob 需手动指定第三参数;构造 blob 必须显式设置 type,否则影响解析;filereader 读取两者一致,但需注意 blob 生命周期和实例复用问题。

有区别,而且这个区别直接影响你能不能拿到文件名、要不要手动设 MIME 类型、甚至影响上传时后端能否正确解析。
File 是 Blob 的子类,但多了 name 和 lastModified
File 对象在继承 Blob 所有属性(size、type、slice())的同时,额外携带了操作系统级的元信息:
-
name:用户选择文件时的真实文件名(比如"avatar.jpg"),Blob没有这个属性,值为"" -
lastModified:毫秒时间戳,Blob无法提供该信息 -
webkitRelativePath(非标准,仅部分浏览器支持):仅 File 可能带路径前缀
这意味着:如果你用 input[type="file"] 拿到的是 File,直接取 file.name 就行;但如果是通过 fetch() 响应生成的 Blob,或调用 new Blob([...]) 构造的,你就得自己补 name —— 否则上传到后端时,FormData.append('file', blob) 会丢失原始文件名,后端可能只收到一个叫 "blob" 的临时文件。
上传时 FormData.append() 对 File 和 Blob 行为不同
虽然两者都能传,但浏览器对它们的默认处理逻辑不一样:
- 传
File:自动把file.name作为Content-Disposition中的filename字段 - 传
Blob:不带filename,除非你显式指定第三个参数:formData.append('file', blob, 'report.pdf') - 漏掉第三个参数的
Blob上传,后端(如 Express 的multer、Django 的request.FILES)可能无法识别文件名,导致保存失败或覆盖
常见错误现象:console.log(file instanceof Blob) 返回 true,就以为“能当 File 用”,结果上传后后端收不到 filename —— 因为 instanceof Blob 对 File 总是成立,但反过不成立。
构造新 Blob 时 type 参数不能省,尤其涉及文本内容
new Blob(['hello'], { type: 'text/plain' }) 和 new Blob(['hello']) 看似一样,但后者 blob.type 是空字符串 '',这会导致:
- 调用
blob.text()或blob.arrayBuffer()没问题,但 - 转成
URL.createObjectURL(blob)后用于<img>、<audio></audio>、<iframe></iframe>时,浏览器可能无法正确识别格式(比如type: ''的 PNG 数据不会渲染) - 某些后端校验 MIME 类型时会拒绝空
type的上传
建议始终显式传 type,哪怕只是 'application/octet-stream'。别依赖浏览器“猜”——它基本不猜。
FileReader 读取 File 和 Blob 完全一致,但读取时机有坑
FileReader 不区分 File 还是 Blob,都走同一套异步流程。但容易忽略的是:
-
File可以直接从input.files[0]获取,而Blob往往是中间产物(比如 canvas.toBlob()、response.blob()),要注意生命周期 - 如果在
FileReader.onload外部立即调用URL.createObjectURL(blob),没问题;但如果 blob 来自canvas.toBlob(callback),回调里的callback参数是Blob,但 canvas 元素本身可能已被重绘,此时再读取 canvas 内容已不同步 -
FileReader实例不能复用:每次读新数据必须新建实例,否则onload可能不触发
真正容易被忽略的点:你拿到一个 Blob,想确认它是不是图片,不要只看 blob.type.startsWith('image/') —— 这个 type 是构造时传入的,可能被伪造或遗漏;更可靠的方式是用 blob.arrayBuffer() 读前几个字节,比对 PNG/JPEG 文件签名(89 50 4E 47 / FF D8 FF)。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











