Blob, File, and Object URLs
Blobs, File objects, object URLs, and revokeObjectURL — previews, downloads, and memory leaks from forgotten URLs.
- javascript
- blob
- file
- object-url
A Blob is an immutable blob of bytes with a MIME type. A File is a Blob that also has name and lastModified — what <input type="file"> and drag-and-drop give you. Object URLs turn those bytes into something an <img> or <a download> can point at without a server round-trip.
Blob and File
const blob = new Blob(['hello, world'], { type: 'text/plain' });
blob.size; // 12
blob.type; // 'text/plain'
const file = new File(['print("hi")'], 'main.py', {
type: 'text/x-python',
lastModified: Date.now(),
});
file.name; // 'main.py'
file instanceof Blob; // true
Slice without copying the whole thing:
const part = blob.slice(0, 5, 'text/plain'); // 'hello'
Object URLs for previews
const input = document.querySelector('input[type=file]');
input.addEventListener('change', () => {
const file = input.files?.[0];
if (!file) return;
const url = URL.createObjectURL(file);
const img = document.querySelector('#preview');
img.src = url;
// critical: release when done
img.onload = () => URL.revokeObjectURL(url);
});
createObjectURL returns a blob:... string bound to the document. Each call allocates; forgetting revokeObjectURL leaks memory for the lifetime of the page (or longer in some cases).
Downloads from generated data
function downloadJson(data, filename = 'export.json') {
const blob = new Blob([JSON.stringify(data, null, 2)], {
type: 'application/json',
});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
Reading File contents
async function readText(file) {
return file.text(); // also: arrayBuffer(), stream()
}
// older pattern
function readAsDataURL(file) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => resolve(reader.result);
reader.onerror = () => reject(reader.error);
reader.readAsDataURL(file);
});
}
Prefer file.text() / arrayBuffer() over FileReader when you can — less ceremony. Data URLs encode base64 and bloat memory; object URLs are better for large images.
Fetch and blobs
const res = await fetch('/report.pdf');
const blob = await res.blob();
const url = URL.createObjectURL(blob);
window.open(url);
// revoke later when the tab is done
You can also new Response(blob) and build uploads with FormData.
Footguns
| Issue | Detail |
|---|---|
| Unrevoked object URLs | Leak file-backed memory |
| Revoking too early | Broken image if still loading |
| Data URL for multi‑MB files | Huge strings; prefer object URLs |
| Assuming File is transferable everywhere | Structured clone works; some APIs want Blob only |
| MIME type empty | Browser may not preview; set type when constructing |
// React-ish cleanup
useEffect(() => {
if (!file) return;
const url = URL.createObjectURL(file);
setPreview(url);
return () => URL.revokeObjectURL(url);
}, [file]);
Interview answer
“Blob is raw bytes plus a type; File extends Blob with name and lastModified. createObjectURL gives a blob: URL for previews and downloads; I always revokeObjectURL when the preview unmounts or finishes loading. Prefer object URLs over base64 data URLs for large files, and file.text()/arrayBuffer() over FileReader when supported.”
Related
Streaming and media
// build a downloadable CSV without keeping one giant string if you chunk
const parts = ['a,b\n', '1,2\n', '3,4\n'];
const blob = new Blob(parts, { type: 'text/csv' });
// media element
video.src = URL.createObjectURL(file);
video.onloadeddata = () => URL.revokeObjectURL(video.src);
Blob parts can be strings, ArrayBuffers, or other Blobs — the constructor concatenates them. For very large user files, prefer file.stream() piping into a worker or upload rather than await file.arrayBuffer() which doubles peak memory. Object URLs are also valid in fetch(url) for local processing without re-reading the File.
Further reading
Related guides
- AbortControllerCancel fetch and other async work with AbortController and AbortSignal — timeouts, race conditions, and cleanup when components unmount.
- Array find, some, every, includesShort-circuiting array predicates: find, findIndex, some, every, and includes — when to use each and common interview traps.
- Array map, filter, reducemap, filter, and reduce as the core transform toolkit — immutability, chaining costs, reduce patterns, and when a plain loop is clearer.
- Arrow Functions Deep DiveLexical this, no arguments object, no construct, concise bodies — when arrows help and when methods and generators need classic functions.
- Async Iteration and for await...ofAsync iterables, for await...of, and streaming data — how async iterators differ from Promise.all and when to use each.