
If you work with modern web applications, sooner or later you'll need to handle data that isn't simply a JavaScript string or JSON object.
You may need to:
- Preview an uploaded image before sending it to the server.
- Generate a CSV file directly in the browser.
- Download a PDF returned by an API.
- Convert generated data into a file.
- Upload binary data using
fetch(). - Display an image or PDF without saving it first.
- Process audio or video data.
This is where the Blob API becomes extremely useful.
In this article, we'll go beyond the basic definition of Blob.
We'll understand how Blob works, how it relates to File, ArrayBuffer, and typed arrays, and we'll build a complete practical example involving:
Selecting an image → converting it to a Blob → previewing it → uploading it → receiving a Blob from an API → downloading it.
Table of Contents
- What Is a Blob?
- Why Do We Need Blob?
- Creating a Blob
- Blob Parts
- The MIME Type
- Blob Properties
- Reading Blob Data
- Blob.text()
- Blob.arrayBuffer()
- Blob.stream()
- Blob.slice()
- Blob and Object URLs
- Previewing an Image
- Downloading a Blob
- Blob vs File
- Creating a File from a Blob
- Uploading a Blob
- Receiving a Blob from an API
- Complete Practical Example
- Blob vs ArrayBuffer vs Uint8Array
- Blob Streams
- Memory Management
- Common Mistakes
- When Should You Use Blob?
- Final Thoughts
What Is a Blob?
Blob stands for:
Binary Large Object
A Blob represents immutable raw binary data.
That data could represent:
- an image
- a PDF
- a video
- an audio file
- a CSV file
- plain text
- JSON
- arbitrary binary data
For example:
const blob = new Blob(
["Hello, world!"],
{
type: "text/plain"
}
);
We have created a Blob containing:
Hello, world!
and told the browser:
This data is text/plain.
We can inspect it:
console.log(blob.size);
console.log(blob.type);
The result will be approximately:
13
text/plain
Why approximately?
Because size represents the number of bytes, not necessarily the number of JavaScript characters.
For ASCII text such as "Hello, world!", the number happens to match.
For Unicode text, it can be different:
const blob = new Blob(
["Hello 👋"],
{
type: "text/plain"
}
);
console.log(blob.size);
The browser is measuring the encoded binary representation.
Why Do We Need Blob?
JavaScript applications frequently communicate using JSON:
fetch("/api/users")
.then(response => response.json())
.then(data => {
console.log(data);
});
But not everything is JSON.
Imagine your backend returns:
report.pdf
or:
profile-image.jpg
or:
invoice.xlsx
The browser needs a representation for that binary content.
That's where Blob becomes useful.
A simplified flow looks like this:
flowchart LR
A[Server] --> B[Binary Data]
B --> C[Blob]
C --> D[Preview]
C --> E[Download]
C --> F[Upload]
C --> G[Convert to File]
Blob is therefore especially useful at the boundary between:
Web APIs and binary data
Creating a Blob
The Blob constructor has the following general structure:
new Blob(blobParts, options)
For example:
const blob = new Blob(
["Hello Blob!"],
{
type: "text/plain"
}
);
Let's inspect it:
console.log(blob);
You can also inspect:
console.log(blob.size);
console.log(blob.type);
Blob Parts
The first argument is an array of parts.
Those parts can contain different types of data.
For example:
const blob = new Blob([
"Hello ",
"world!"
]);
The resulting Blob contains:
Hello world!
You can also combine multiple strings:
const blob = new Blob([
"Hello\n",
"This is a Blob\n",
"Created in JavaScript."
], {
type: "text/plain"
});
Blob Can Contain Other Blobs
You can also combine existing Blobs:
const first = new Blob(
["Hello "],
{ type: "text/plain" }
);
const second = new Blob(
["world!"],
{ type: "text/plain" }
);
const combined = new Blob([
first,
second
], {
type: "text/plain"
});
Now:
console.log(await combined.text());
Result:
Hello world!
The MIME Type
The type property represents the Blob's MIME type.
For example:
const blob = new Blob(
["Hello"],
{
type: "text/plain"
}
);
Then:
console.log(blob.type);
returns:
text/plain
For an image:
const imageBlob = new Blob(
[imageData],
{
type: "image/png"
}
);
For a PDF:
const pdfBlob = new Blob(
[pdfData],
{
type: "application/pdf"
}
);
For JSON:
const jsonBlob = new Blob(
[JSON.stringify({
name: "Abanoub"
})],
{
type: "application/json"
}
);
Common MIME types include:
| Content | MIME Type |
|---|---|
| Plain text | text/plain |
| HTML | text/html |
| JSON | application/json |
application/pdf |
|
| PNG | image/png |
| JPEG | image/jpeg |
| GIF | image/gif |
| WebP | image/webp |
| CSV | text/csv |
| ZIP | application/zip |
| MP3 | audio/mpeg |
| MP4 | video/mp4 |
The MIME type doesn't magically transform the data.
It is metadata describing what the data represents.
Blob Properties
A Blob mainly exposes two important properties.
size
size returns the size of the Blob in bytes.
const blob = new Blob(
["Hello"]
);
console.log(blob.size);
Result:
5
type
type returns the MIME type.
const blob = new Blob(
["Hello"],
{
type: "text/plain"
}
);
console.log(blob.type);
Result:
text/plain
Reading Blob Data
Creating a Blob is only part of the story.
Eventually, you usually need to consume the data.
The most important methods are:
blob.text()
blob.arrayBuffer()
blob.stream()
Blob.text()
Use text() when the Blob contains textual data.
Example:
const blob = new Blob(
["Hello from Blob!"],
{
type: "text/plain"
}
);
const text = await blob.text();
console.log(text);
Output:
Hello from Blob!
Because text() returns a Promise, you can use:
blob.text().then(text => {
console.log(text);
});
Or preferably:
const text = await blob.text();
Reading JSON from a Blob
Suppose you have:
const data = {
name: "Abanoub",
role: "Frontend Developer"
};
const blob = new Blob(
[JSON.stringify(data)],
{
type: "application/json"
}
);
You can read it:
const text = await blob.text();
const parsed = JSON.parse(text);
console.log(parsed.name);
console.log(parsed.role);
Output:
Abanoub
Frontend Developer
Blob.arrayBuffer()
If you need the actual binary representation, use:
blob.arrayBuffer()
Example:
const blob = new Blob(
["Hello"],
{
type: "text/plain"
}
);
const buffer = await blob.arrayBuffer();
console.log(buffer);
You get an:
ArrayBuffer
This becomes particularly useful when working with:
- binary protocols
- cryptography
- Web APIs
- Web Audio
- WebSockets
- binary file formats
- typed arrays
Blob → ArrayBuffer → Uint8Array
A common pattern is:
const buffer = await blob.arrayBuffer();
const bytes = new Uint8Array(buffer);
console.log(bytes);
For "Hello" you'll see byte values similar to:
[72, 101, 108, 108, 111]
The relationship is:
flowchart LR
A[Blob] -->|arrayBuffer| B[ArrayBuffer]
B --> C[Uint8Array]
C --> D[Individual Bytes]
Blob.stream()
A Blob can also expose its data as a ReadableStream.
const blob = new Blob(
["Hello from Blob"],
{
type: "text/plain"
}
);
const stream = blob.stream();
console.log(stream);
This becomes useful when dealing with streaming APIs and large amounts of data.
We'll return to this later.
Blob.slice()
slice() creates a new Blob containing a portion of the original Blob.
Example:
const blob = new Blob(
["Hello World"],
{
type: "text/plain"
}
);
const part = blob.slice(0, 5);
console.log(await part.text());
Result:
Hello
The syntax is:
blob.slice(start, end, contentType)
For example:
const part = blob.slice(
0,
5,
"text/plain"
);
This is useful when you need to work with only a portion of binary data.
Blob and Object URLs
One of the most useful concepts associated with Blob is:
URL.createObjectURL()
Suppose you have:
const blob = new Blob(
["Hello"],
{
type: "text/plain"
}
);
You can create a temporary URL:
const url = URL.createObjectURL(blob);
console.log(url);
You'll get something similar to:
blob:https://example.com/550e8400-e29b-41d4-a716-446655440000
This URL doesn't represent a file on your server.
It represents the Blob in the browser.
Think of It Like This
flowchart LR
A[Blob in Memory] --> B[URL.createObjectURL]
B --> C[blob:// URL]
C --> D[img]
C --> E[a]
C --> F[iframe]
C --> G[video]
This is extremely useful.
Previewing an Image
Let's say the user selects an image.
HTML:
<input
type="file"
id="imageInput"
accept="image/*"
/>
<img
id="preview"
width="300"
/>
JavaScript:
const input = document.getElementById("imageInput");
const preview = document.getElementById("preview");
input.addEventListener("change", () => {
const file = input.files[0];
if (!file) {
return;
}
const url = URL.createObjectURL(file);
preview.src = url;
});
Notice something important:
File is already a kind of Blob.
So we don't need to manually convert the selected file into another Blob just to preview it.
Blob URLs Are Temporary
An object URL consumes browser resources.
When you're finished with it, release it:
URL.revokeObjectURL(url);
For example:
const url = URL.createObjectURL(file);
preview.src = url;
preview.onload = () => {
URL.revokeObjectURL(url);
};
This becomes important when you're creating many object URLs.
Downloading a Blob
One of the most common Blob use cases is generating a file dynamically.
Let's generate a text file.
const content = `
Name: Abanoub
Role: Frontend Developer
`;
const blob = new Blob(
[content],
{
type: "text/plain"
}
);
Create an object URL:
const url = URL.createObjectURL(blob);
Create a link:
const link = document.createElement("a");
link.href = url;
link.download = "profile.txt";
link.click();
Finally:
URL.revokeObjectURL(url);
Complete example:
function downloadTextFile() {
const content = `
Name: Abanoub
Role: Frontend Developer
`;
const blob = new Blob(
[content],
{
type: "text/plain"
}
);
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "profile.txt";
link.click();
URL.revokeObjectURL(url);
}
This creates a real downloadable file without sending anything to a server.
Generating a CSV File
Blob becomes particularly useful for exporting data.
Imagine:
const users = [
{
name: "Abanoub",
email: "abanoub@example.com"
},
{
name: "John",
email: "john@example.com"
}
];
We can generate CSV:
const csv = [
"name,email",
...users.map(user =>
`${user.name},${user.email}`
)
].join("\n");
Then:
const blob = new Blob(
[csv],
{
type: "text/csv;charset=utf-8"
}
);
Create the download:
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "users.csv";
link.click();
URL.revokeObjectURL(url);
The browser will download:
users.csv
Blob vs File
This is one of the most important distinctions.
A File is essentially a specialized kind of Blob.
Conceptually:
Blob
↑
|
File
A Blob primarily represents binary data.
A File represents binary data plus file-oriented metadata, such as:
- filename
- last modified timestamp
For example:
const file = new File(
["Hello"],
"hello.txt",
{
type: "text/plain"
}
);
We can inspect:
console.log(file.name);
console.log(file.size);
console.log(file.type);
console.log(file.lastModified);
File Is a Blob
You can check:
const file = new File(
["Hello"],
"hello.txt",
{
type: "text/plain"
}
);
console.log(file instanceof Blob);
Result:
true
That's why APIs that accept Blob often also accept File.
For example:
function processBlob(blob) {
console.log(blob.size);
}
You can pass:
processBlob(file);
because File is a Blob.
Creating a File from a Blob
Suppose we already have:
const blob = new Blob(
["Hello from JavaScript"],
{
type: "text/plain"
}
);
We can turn it into a File:
const file = new File(
[blob],
"hello.txt",
{
type: blob.type
}
);
Now:
console.log(file.name);
returns:
hello.txt
Uploading a Blob
You can send a Blob directly using fetch().
Example:
const blob = new Blob(
["Hello from the browser"],
{
type: "text/plain"
}
);
const response = await fetch(
"/api/upload",
{
method: "POST",
body: blob
}
);
The request body contains the Blob data.
You can also explicitly specify headers:
await fetch(
"/api/upload",
{
method: "POST",
headers: {
"Content-Type": "text/plain"
},
body: blob
}
);
Uploading a File Using FormData
For normal file uploads, FormData is often more convenient.
HTML:
<input
type="file"
id="fileInput"
/>
JavaScript:
const input = document.getElementById("fileInput");
const file = input.files[0];
const formData = new FormData();
formData.append(
"file",
file
);
await fetch(
"/api/upload",
{
method: "POST",
body: formData
}
);
Notice that we don't manually set:
Content-Type
when using FormData.
The browser needs to generate the multipart boundary automatically.
Receiving a Blob from an API
This is another extremely important use case.
Suppose your backend exposes:
GET /api/report
and returns a PDF.
Instead of:
const data = await response.json();
you can do:
const response = await fetch(
"/api/report"
);
const blob = await response.blob();
Now you have the PDF as a Blob.
You can download it.
const url = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = url;
link.download = "report.pdf";
link.click();
URL.revokeObjectURL(url);
Opening a PDF
You don't necessarily need to download it.
You can create an object URL:
const response = await fetch(
"/api/report"
);
const blob = await response.blob();
const url = URL.createObjectURL(blob);
Then:
window.open(url);
Or put it inside an iframe:
<iframe id="pdfViewer"></iframe>
const iframe = document.getElementById(
"pdfViewer"
);
iframe.src = url;
Complete Practical Example
Now let's build a small application that demonstrates the Blob workflow.
Our application will allow the user to:
- Select an image.
- Preview it.
- Convert/process the image as Blob data.
- Display Blob metadata.
- Download the image Blob.
HTML
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta
name="viewport"
content="width=device-width, initial-scale=1.0"
>
<title>Blob Demo</title>
</head>
<body>
<h1>Blob Image Demo</h1>
<input
type="file"
id="imageInput"
accept="image/*"
/>
<br><br>
<img
id="preview"
width="400"
alt="Preview"
>
<p>
Size:
<span id="size"></span>
</p>
<p>
Type:
<span id="type"></span>
</p>
<button id="downloadButton">
Download
</button>
<script src="app.js"></script>
</body>
</html>
JavaScript
const imageInput =
document.getElementById("imageInput");
const preview =
document.getElementById("preview");
const sizeElement =
document.getElementById("size");
const typeElement =
document.getElementById("type");
const downloadButton =
document.getElementById("downloadButton");
let currentBlob = null;
let currentUrl = null;
When the user selects an image:
imageInput.addEventListener(
"change",
() => {
const file =
imageInput.files[0];
if (!file) {
return;
}
currentBlob = file;
sizeElement.textContent =
`${file.size} bytes`;
typeElement.textContent =
file.type;
if (currentUrl) {
URL.revokeObjectURL(currentUrl);
}
currentUrl =
URL.createObjectURL(file);
preview.src =
currentUrl;
}
);
Then implement downloading:
downloadButton.addEventListener(
"click",
() => {
if (!currentBlob) {
alert("Please select an image first.");
return;
}
const url =
URL.createObjectURL(currentBlob);
const link =
document.createElement("a");
link.href = url;
link.download =
"downloaded-image";
link.click();
URL.revokeObjectURL(url);
}
);
Understanding What Happened
The complete flow is:
flowchart TD
A[User selects image] --> B[File object]
B --> C[File is a Blob]
C --> D[URL.createObjectURL]
D --> E[Blob URL]
E --> F[Image Preview]
C --> G[Download]
G --> H[Browser saves file]
The important point is that we didn't upload anything to the server.
The browser already has the file data.
We simply created a temporary URL pointing to that data.
A More Interesting Example: Generate an Image Blob
We can also create Blob data ourselves.
For example, using a Canvas:
const canvas =
document.createElement("canvas");
canvas.width = 500;
canvas.height = 300;
const context =
canvas.getContext("2d");
context.fillStyle = "black";
context.fillRect(
0,
0,
500,
300
);
Now draw some text:
context.fillStyle = "white";
context.font =
"30px Arial";
context.fillText(
"Hello Blob!",
150,
150
);
We can convert the canvas to a Blob:
canvas.toBlob(
blob => {
console.log(blob);
},
"image/png"
);
Now we have:
Canvas
↓
PNG Blob
Canvas → Blob → Download
We can combine everything:
canvas.toBlob(
blob => {
if (!blob) {
return;
}
const url =
URL.createObjectURL(blob);
const link =
document.createElement("a");
link.href = url;
link.download =
"generated-image.png";
link.click();
URL.revokeObjectURL(url);
},
"image/png"
);
This is a very practical browser capability.
You can generate an image entirely on the client and download it without a backend.
Blob and fetch()
The Fetch API provides several ways to consume a response.
For example:
const response = await fetch("/api/data");
You might use:
await response.json();
for JSON.
Or:
await response.text();
for text.
Or:
await response.blob();
for binary/file-like data.
Or:
await response.arrayBuffer();
for lower-level binary processing.
A useful mental model is:
flowchart TD
A[HTTP Response] --> B[JSON]
A --> C[Text]
A --> D[Blob]
A --> E[ArrayBuffer]
Choose the representation based on what you need to do with the response.
Blob vs ArrayBuffer vs Uint8Array
This is an area that often confuses developers.
These concepts are related, but they serve different purposes.
Blob
Think:
"I have some data that I want to treat as a file-like binary object."
Example:
const blob = new Blob(
[data],
{
type: "image/png"
}
);
Great for:
- files
- downloads
- previews
- uploads
- media
- browser APIs
ArrayBuffer
Think:
"I need access to raw binary memory."
Example:
const buffer =
await blob.arrayBuffer();
An ArrayBuffer represents a fixed-length block of binary memory.
It doesn't tell you what those bytes mean.
Uint8Array
Think:
"I want to access the individual bytes."
Example:
const bytes =
new Uint8Array(buffer);
console.log(bytes[0]);
console.log(bytes[1]);
Comparison
| API | Main Purpose |
|---|---|
| Blob | File-like binary data |
| File | Blob + filename/metadata |
| ArrayBuffer | Raw binary memory |
| Uint8Array | View/access individual bytes |
A useful conceptual relationship is:
flowchart LR
A[Blob] -->|arrayBuffer| B[ArrayBuffer]
B --> C[Uint8Array]
But don't think of these as simply different names for the same thing.
They represent different abstractions.
Blob Streams
For large data, streaming can be important.
A Blob can produce a readable stream:
const blob = new Blob([
"Large amount of data..."
]);
const stream =
blob.stream();
You can consume it using a reader:
const reader =
stream.getReader();
while (true) {
const {
done,
value
} = await reader.read();
if (done) {
break;
}
console.log(value);
}
The value contains chunks of data.
This is useful when you don't want to conceptually treat the entire resource as one immediately-consumed value.
Memory Management
Blob-related code often involves browser-managed resources.
One particularly important API is:
URL.createObjectURL()
Whenever you create:
const url =
URL.createObjectURL(blob);
you should eventually consider:
URL.revokeObjectURL(url);
For example:
const url =
URL.createObjectURL(blob);
image.src = url;
image.onload = () => {
URL.revokeObjectURL(url);
};
Why Does revokeObjectURL() Matter?
Imagine a UI where users continuously select images:
Image 1 → createObjectURL()
Image 2 → createObjectURL()
Image 3 → createObjectURL()
Image 4 → createObjectURL()
...
If you keep creating object URLs and never release them, the browser may retain resources longer than necessary.
A better pattern is:
if (currentUrl) {
URL.revokeObjectURL(currentUrl);
}
currentUrl =
URL.createObjectURL(newBlob);
This is especially important in:
- image galleries
- file managers
- video previews
- drag-and-drop interfaces
- React applications
- Angular applications
- long-lived single-page applications
Common Mistake #1: Confusing Blob With File
A Blob is not necessarily a file on disk.
This:
const blob = new Blob([
"Hello"
]);
doesn't mean you created:
hello.txt
on the user's filesystem.
You've created binary data represented by a JavaScript object.
To create a downloadable file, you need to explicitly provide a filename through the download mechanism or create a File object.
Common Mistake #2: Forgetting the MIME Type
You can technically create:
const blob = new Blob([
data
]);
But if the consuming API needs to know what kind of data it is, specify the type:
const blob = new Blob(
[data],
{
type: "application/pdf"
}
);
or:
const blob = new Blob(
[data],
{
type: "image/png"
}
);
Common Mistake #3: Forgetting to Revoke Object URLs
Avoid repeatedly doing:
const url =
URL.createObjectURL(blob);
without eventually releasing the URL when appropriate.
Use:
URL.revokeObjectURL(url);
Common Mistake #4: Manually Setting Content-Type with FormData
This is a common mistake:
fetch("/upload", {
method: "POST",
headers: {
"Content-Type": "multipart/form-data"
},
body: formData
});
Don't manually set this header in normal browser FormData uploads.
Use:
fetch("/upload", {
method: "POST",
body: formData
});
The browser generates the correct multipart boundary.
Common Mistake #5: Assuming Blob Automatically Compresses Data
Blob does not mean:
compressed data
A Blob is simply a representation of binary data.
For example:
new Blob([
hugeString
]);
doesn't automatically compress hugeString.
Compression is a separate concern.
Common Mistake #6: Converting Everything to Base64
Developers sometimes use:
Binary data → Base64 → JSON
for everything.
This can be convenient in some APIs, but it isn't always the best choice.
Base64 introduces additional representation overhead and often makes binary transfer less direct.
For file operations, Blob/File plus fetch() or FormData is often a much more natural browser-side approach.
Blob in React
The Blob API isn't specific to vanilla JavaScript.
You can use it directly inside React.
Example:
function ImagePreview() {
const [url, setUrl] =
React.useState(null);
const handleChange = event => {
const file =
event.target.files[0];
if (!file) {
return;
}
const objectUrl =
URL.createObjectURL(file);
setUrl(objectUrl);
};
return (
<>
<input
type="file"
accept="image/*"
onChange={handleChange}
/>
{url && (
<img
src={url}
width="300"
alt="Preview"
/>
)}
</>
);
}
In a production React application, you should also clean up the object URL appropriately.
Blob in Angular
The same browser APIs are available in Angular.
For example:
this.http.get(
'/api/report',
{
responseType: 'blob'
}
);
Then:
this.http.get(
'/api/report',
{
responseType: 'blob'
}
).subscribe(blob => {
const url =
URL.createObjectURL(blob);
window.open(url);
});
This is one of the most common Blob patterns in Angular applications.
A Complete API Download Example
Suppose the backend provides:
GET /api/reports/monthly
and returns:
application/pdf
The frontend can do:
async function downloadReport() {
const response =
await fetch(
"/api/reports/monthly"
);
if (!response.ok) {
throw new Error(
"Failed to download report"
);
}
const blob =
await response.blob();
const url =
URL.createObjectURL(blob);
const link =
document.createElement("a");
link.href = url;
link.download =
"monthly-report.pdf";
document.body.appendChild(link);
link.click();
link.remove();
URL.revokeObjectURL(url);
}
This pattern is extremely common in real-world applications.
A Complete Upload + Preview Workflow
Let's put the concepts together.
flowchart TD
A[User selects File] --> B[File object]
B --> C[File is a Blob]
C --> D[createObjectURL]
D --> E[Image Preview]
C --> F[FormData]
F --> G[POST /upload]
G --> H[Backend]
H --> I[Stored File]
H --> J[GET /download]
J --> K[Response Blob]
K --> L[Object URL]
L --> M[Download]
This is the architecture behind many file upload/download interfaces.
When Should You Use Blob?
Blob is a good choice when you're dealing with browser-side binary or file-like data.
Use Blob when you need to:
Preview files
URL.createObjectURL(file)
Download generated data
new Blob([...])
Download API responses
response.blob()
Upload binary data
fetch(url, {
method: "POST",
body: blob
});
Generate files dynamically
new Blob([
csv
]);
Work with media
Images, audio, and video frequently interact with Blob/File APIs.
When Should You Use ArrayBuffer Instead?
Use ArrayBuffer when you need lower-level binary access.
For example:
const buffer =
await response.arrayBuffer();
This becomes more appropriate when you're manipulating the bytes themselves.
Examples include:
- cryptography
- binary protocols
- binary file parsing
- WebSockets
- Web Audio
- low-level encoding/decoding
A useful rule is:
Blob is file-oriented; ArrayBuffer is byte-oriented.
It's not an absolute rule, but it's a very useful mental model.
Blob Is Not Just "A File"
This is probably the most important idea to take away.
A Blob doesn't necessarily represent a physical file.
Instead, think of it as:
An immutable container for binary data that the browser can consume as file-like data.
That distinction explains why you can create a Blob from:
const blob = new Blob([
"Hello"
]);
without creating anything on the user's disk.
You can then decide what you want to do with it:
Blob
├── Preview
├── Download
├── Upload
├── Read as text
├── Read as ArrayBuffer
├── Stream
└── Convert to File
A Practical Mental Model
When working with browser file APIs, think about the following layers:
flowchart TD
A[Data] --> B[Blob]
B --> C[Object URL]
C --> D[Browser Resource]
B --> E[File]
E --> F[File Upload]
B --> G[ArrayBuffer]
G --> H[Byte-Level Processing]
Each abstraction has a different job.
Blob
File-like binary data.
File
Blob + filename and file metadata.
Object URL
A temporary browser URL that points to Blob/File data.
ArrayBuffer
Raw binary memory.
Uint8Array
A byte-oriented view over binary memory.
Final Example: Everything Together
Here is a compact example that demonstrates several concepts at once.
<input
type="file"
id="fileInput"
/>
<img
id="preview"
width="300"
/>
<button id="download">
Download
</button>
const fileInput =
document.getElementById("fileInput");
const preview =
document.getElementById("preview");
const downloadButton =
document.getElementById("download");
let selectedFile = null;
let previewUrl = null;
fileInput.addEventListener(
"change",
() => {
const file =
fileInput.files[0];
if (!file) {
return;
}
selectedFile = file;
if (previewUrl) {
URL.revokeObjectURL(previewUrl);
}
previewUrl =
URL.createObjectURL(file);
preview.src =
previewUrl;
}
);
downloadButton.addEventListener(
"click",
() => {
if (!selectedFile) {
return;
}
const url =
URL.createObjectURL(
selectedFile
);
const link =
document.createElement("a");
link.href = url;
link.download =
selectedFile.name;
link.click();
URL.revokeObjectURL(url);
}
);
The important flow is:
File
↓
Blob
↓
Object URL
↓
Preview
and:
File
↓
Blob
↓
Object URL
↓
Download
Final Thoughts
The Blob API looks simple at first:
new Blob(...)
but it is actually one of the fundamental browser APIs for working with binary data.
Once you understand Blob, several other browser concepts become much easier to understand:
FileFileReaderFormDataURL.createObjectURL()fetch().blob()ArrayBufferUint8Array- file uploads
- file downloads
- image previews
- generated files
- API-generated PDFs
- browser-side media processing
The most important mental model is:
Blob = immutable binary data
Then:
File = Blob + file metadata
and:
Object URL = temporary browser URL pointing to Blob/File data
while:
ArrayBuffer = raw binary memory
and:
Uint8Array = byte-level view of that memory
Once these distinctions are clear, working with files and binary data in JavaScript becomes much less mysterious.
Quick Cheat Sheet
// Create Blob
const blob = new Blob(
["Hello"],
{
type: "text/plain"
}
);
// Size
console.log(blob.size);
// MIME type
console.log(blob.type);
// Read as text
const text = await blob.text();
// Read as binary memory
const buffer =
await blob.arrayBuffer();
// Create temporary URL
const url =
URL.createObjectURL(blob);
// Release temporary URL
URL.revokeObjectURL(url);
// Convert Blob to File
const file = new File(
[blob],
"hello.txt",
{
type: blob.type
}
);
// Get Blob from Fetch response
const response =
await fetch("/file");
const downloadedBlob =
await response.blob();
If you understand these APIs, you already have the foundation needed for most browser-side file and binary-data workflows.
Top comments (0)