FileReader读取文件后需用URL.createObjectURL生成临时URL,直接设a.href=file.path会失效;推荐直接传File对象创建URL,更高效且避免Base64开销;用完须调用URL.revokeObjectURL释放内存。

FileReader 读取上传文件后生成 blob URL
用户选中文件后,浏览器不会直接暴露本地路径(出于安全限制),必须通过 FileReader 读取为 Blob 或 ArrayBuffer,再用 URL.createObjectURL() 创建临时下载链接。跳过这步直接设 a.href = file.path 会失效——那只是空字符串或 fakepath。
关键点:
-
FileReader.readAsArrayBuffer()适合二进制文件(如图片、PDF),readAsDataURL()会产生 base64 开销大,不推荐用于大文件 - 生成的
blob:URL 只在当前页面生命周期有效,页面刷新即失效 - 用完应主动调用
URL.revokeObjectURL()避免内存泄漏,尤其在频繁上传/预览场景
const input = document.querySelector('input[type="file"]');
input.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
<p>const reader = new FileReader();
reader.onload = () => {
const blob = new Blob([reader.result], { type: file.type });
const url = URL.createObjectURL(blob);</p><pre class="brush:php;toolbar:false;">const a = document.createElement('a');
a.href = url;
a.download = file.name;
a.click();
// 清理:稍后释放,避免堆积
setTimeout(() => URL.revokeObjectURL(url), 100);}; reader.readAsArrayBuffer(file); // 比 readAsDataURL 更高效 });
直接用 File 对象创建 URL(更简单)
其实不需要读取内容——只要下载原文件,File 本身就是 Blob 的子类,可直接传给 URL.createObjectURL()。省去 FileReader 步骤,更快、无编码损耗、支持任意大小(浏览器内部处理)。
常见误操作:
- 对
File调用readAsText()再构造Blob:纯属冗余,且可能破坏二进制数据 - 忘记设
a.download属性:链接能打开,但不会触发下载,而是尝试在标签页渲染(比如 PDF 就直接预览了) - 在非用户手势(如 setTimeout)中触发
a.click():现代浏览器会拦截,必须由input.change等事件同步发起
input.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
<p>const url = URL.createObjectURL(file); // 直接传 File!
const a = document.createElement('a');
a.href = url;
a.download = file.name; // 必须显式指定,否则用 URL 路径名
a.click();</p><p>URL.revokeObjectURL(url); // 可立即释放,因为 click 是同步完成的
});</p>
下载失败或打不开的典型原因
点击链接没反应,或下载后文件损坏,大概率是以下之一:
-
a.download属性缺失或为空:某些浏览器(如 Safari)严格要求该属性存在才触发下载 - 跨域限制:如果后续要把这个 URL 传给后端或 iframe,
blob:协议不支持跨域请求,也不能被 fetch 直接读取 - 文件名含非法字符:Windows 对
/ : * ? " |敏感,建议用正则清理file.name.replace(/[\/:*?"|]/g, '_') - 移动端 Safari 不支持
a.download:需降级为window.open(url),但无法控制文件名
大文件或需要流式处理时怎么办
上面方法依赖内存缓存整个 Blob,几 MB 以上就明显卡顿。真要处理 GB 级文件(如视频剪辑导出),得用 ReadableStream + Response 构造流式响应,再配合 fetch() 和 response.body.pipeTo() 写入 WritableStream(如 FileSystemWritableFileStream)。但这已超出“前端生成下载链接”范畴——本质是客户端保存,而非传统 HTTP 下载。
简单项目里,别硬扛。超过 50MB 就该提醒用户:“文件较大,建议用服务器生成下载地址”。URL.createObjectURL(file) 的内存和兼容性边界,很容易被忽略。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











