URL.createObjectURL 是实现本地图片即时预览最轻量、最直接的方式,它基于 Blob 生成临时 URL,需手动调用 revokeObjectURL 释放内存,否则会导致持续内存泄漏。

URL.createObjectURL 是实现本地图片即时预览最轻量、最直接的方式——它不需要上传服务器,不依赖第三方库,且兼容所有现代浏览器(包括 Safari 15.4+)。但用错参数或忽略清理,会导致内存泄漏或预览失效。
为什么不能直接用 file.path 或 readAsDataURL
用户常误以为能直接把 input[type="file"] 的 files[0].path 当作本地路径使用,但浏览器出于安全限制,该属性在大多数环境下为空或不可读;而 readAsDataURL 虽可行,但会生成冗长 base64 字符串,拖慢渲染、增加内存占用,且无法被浏览器缓存复用。相比之下,URL.createObjectURL 返回的是一个指向内存中 Blob 的临时 URL,轻量、同步、可直接赋值给 img.src。
- 仅接受
Blob或File实例(File是Blob的子类) - 返回的 URL 形如
blob:https://example.com/3a7b8c...,仅当前文档生命周期内有效 - 必须手动调用
URL.revokeObjectURL释放引用,否则对象持续驻留内存
input[type="file"] 选中后如何正确生成预览 URL
关键在于监听 change 事件,并从 event.target.files 中取到 File 对象。注意:不要在循环里反复调用 createObjectURL 却不 revoke 旧 URL,尤其当用户多次选择不同文件时。
const input = document.querySelector('input[type="file"]');
const img = document.querySelector('img#preview');
input.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file || !file.type.startsWith('image/')) return;
// 先释放上一次的 URL(如有)
if (img.src) URL.revokeObjectURL(img.src);
// 创建新 URL 并赋值
img.src = URL.createObjectURL(file);
});
- 务必校验
file.type,避免非图片类型(如 PDF)触发img加载失败但无提示 - 若支持多图预览,需为每个
File单独创建 URL,并维护对应关系(例如用Map记录file → url) - 不要在
img.onload里再调用createObjectURL——它本就是同步操作,无需等待
预览后忘记 revokeObjectURL 会怎样
每调用一次 URL.createObjectURL,浏览器就会在内存中保留一份该 Blob 的引用,即使 DOM 中已移除 img 元素,只要 URL 还在 src 或变量中持有,Blob 就不会被 GC。连续选 10 张 5MB 图片,可能悄悄吃掉 50MB 内存。
- 最佳释放时机:下次创建新 URL 前,或页面卸载前(加
beforeunload监听) - 如果预览图被销毁(例如 Vue 组件 unmount、React useEffect cleanup),必须同步
revoke - Chrome DevTools 的
Memory > Allocation instrumentation on timeline可观察 Blob 是否持续增长
真正容易被忽略的不是怎么创建 URL,而是谁负责销毁它——尤其是组件化场景下,创建和销毁常不在同一作用域。一旦漏掉 revoke,预览功能越用越卡,但错误日志里什么都不会报。

















