While ArrayBuffer and TypedArrays give you direct access to bytes in memory, the Blob and File APIs operate at a higher level of abstraction. A Blob represents an immutable chunk of binary data that may not even reside in memory — it could be backed by a file on disk or data streamed from the network. The File API extends Blob for user-selected files. Together, they provide the tools for creating downloadable files, processing uploads, and streaming large binary data without loading everything into memory at once.
Key Concepts
- Blob: An immutable, opaque object representing raw binary data. It has a
size(in bytes) and atype(MIME type), but its contents are not directly accessible as a JavaScript array. - File: A subclass of Blob that adds
name,lastModified, and other metadata. Created by file input elements and drag-and-drop events. - FileReader: An older, callback-based API for reading Blob/File contents as text, ArrayBuffer, or data URL. Largely superseded by
Blob.prototype.text(),arrayBuffer(), andstream(). - URL.createObjectURL: Creates a temporary URL pointing to a Blob's data, enabling it to be used as an
<img>src,<a>href, or<video>source. - Blob Slicing:
Blob.prototype.slice(start, end, type)creates a new Blob referencing a byte range of the original without copying data. - Streams API:
Blob.prototype.stream()returns aReadableStreamofUint8Arraychunks, enabling incremental processing of large files.
Real World Context
Blob and File are everywhere in web applications. File upload features use the File API to validate and preview user-selected files before sending them to a server. Client-side file generation — exporting CSV reports, saving canvas artwork as PNG, or generating PDF documents — relies on Blob creation and download. Large file processors like video editors and ZIP utilities use Blob.stream() to handle multi-gigabyte files without exhausting memory. Understanding these APIs is essential for any application that creates, consumes, or transforms files in the browser.
Deep Dive
Creating Blobs from Data
The Blob constructor accepts an array of parts (strings, ArrayBuffers, TypedArrays, or other Blobs) and an optional options object with a type property:
javascript// From a string const textBlob = new Blob(['Hello, world!'], { type: 'text/plain' }); // From JSON const jsonBlob = new Blob( [JSON.stringify({ key: 'value' })], { type: 'application/json' } ); // From binary data const bytes = new Uint8Array([0x89, 0x50, 0x4E, 0x47]); const binaryBlob = new Blob([bytes], { type: 'image/png' }); // From multiple parts const multiBlob = new Blob(['Header\n', bytes, '\nFooter']);
Reading Blob Contents
Modern APIs provide promise-based methods directly on Blob:
javascriptconst text = await blob.text(); // String const buffer = await blob.arrayBuffer(); // ArrayBuffer const stream = blob.stream(); // ReadableStream
The older FileReader API uses events and is only needed for legacy browser support or for reading as a data URL (readAsDataURL).
File API for User Uploads
The File object extends Blob with file metadata. You get File objects from <input type="file"> elements and drag-and-drop events:
javascriptinput.addEventListener('change', (e) => { const file = e.target.files[0]; // File object console.log(file.name); // 'photo.jpg' console.log(file.size); // bytes console.log(file.type); // 'image/jpeg' console.log(file.lastModified); // timestamp });
Streaming Large Files
For large files, loading the entire contents into memory is impractical. Blob.stream() returns a ReadableStream that yields Uint8Array chunks, enabling incremental processing:
javascriptasync function hashFile(file) { const stream = file.stream(); const reader = stream.getReader(); let totalBytes = 0; while (true) { const { done, value } = await reader.read(); if (done) break; totalBytes += value.byteLength; // Process each chunk incrementally } return totalBytes; }
Blob URLs and Memory Management
URL.createObjectURL(blob) creates a blob: URL that can be used anywhere a regular URL is expected. This is how you display uploaded images, trigger file downloads, or set video sources without a server round-trip:
javascriptconst url = URL.createObjectURL(blob); img.src = url; // Display the blob as an image
Critically, blob URLs hold a strong reference to the Blob, preventing garbage collection. You must call URL.revokeObjectURL(url) when the URL is no longer needed to release the memory.
Blob Slicing
blob.slice(start, end, contentType) creates a new Blob referencing a byte range of the original. This is a zero-copy operation — no data is duplicated:
javascriptconst header = largeBlob.slice(0, 1024, 'application/octet-stream'); const chunk = largeBlob.slice(1024, 2048);
This is useful for chunked uploads, where you send a large file in pieces, and for parsing file headers without reading the entire file.
Common Pitfalls
- Leaking Blob URLs: Every
createObjectURLcall allocates a URL that persists until revoked or the document unloads. In single-page applications with frequent file previews, this causes steady memory growth. Always paircreateObjectURLwithrevokeObjectURL. - Blocking the main thread with large reads: Calling
blob.arrayBuffer()on a multi-gigabyte file will attempt to allocate that much memory at once, potentially crashing the tab. Use streaming for large files. - Assuming Blob is synchronous: Blob contents are not immediately accessible. All read operations are asynchronous. Attempting to use a Blob like an ArrayBuffer will fail.
Best Practices
- Use
Blob.stream()for files over a few megabytes. Streaming avoids memory spikes and enables progress reporting. - Revoke Blob URLs immediately after use. In a download scenario, revoke the URL right after triggering the click. For image previews, revoke in the
onloadhandler. - Prefer the modern promise-based Blob methods (
text(),arrayBuffer(),stream()) overFileReader. They produce cleaner code and integrate naturally withasync/await.
Summary
The Blob API provides an immutable, high-level interface for binary data that abstracts away memory management details. The File API extends it for user-selected files. Together with URL.createObjectURL for display, Blob.stream() for incremental processing, and slice() for zero-copy subsetting, they form a complete toolkit for client-side file creation, upload handling, and large-data processing. Always revoke Blob URLs to prevent memory leaks, and prefer streaming over bulk reads for large files.
Code Examples
// Create and download a file
const data = JSON.stringify({ name: 'test' }, null, 2);
const blob = new Blob([data], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'data.json';
a.click();
URL.revokeObjectURL(url); // Free memory
// Stream a large file
async function processLargeFile(file) {
const stream = file.stream();
const reader = stream.getReader();
let totalBytes = 0;
while (true) {
const { done, value } = await reader.read();
if (done) break;
totalBytes += value.byteLength;
processChunk(value); // Uint8Array chunk
}
console.log('Processed', totalBytes, 'bytes');
}