Modern web apps often need to work with local files—previewing an image before upload, importing a CSV, or reading a text document to populate an editor. The File API is the foundational Web API that makes this possible by letting a page read files the user explicitly provides.
What the File API is for
The File API enables web applications to access files and their contents when the user makes them available, typically through:
- An HTML file input: <input type="file">
- Drag and drop (via the drag-and-drop APIs)
When files are provided this way, the browser exposes them to your code as a FileList, which contains one or more File objects.
The core building blocks
- FileList: What you get from an <input type="file">’s .files property (and also from drag-and-drop). It’s essentially the list of selected/dropped files.
- File: A file-like object with useful metadata—name, size, type, and last modified date—and access to the file’s contents.
- Blob: A “Binary Large Object,” representing immutable raw data. Files are also blobs, and blobs can be read as text or binary (or converted to streams).
- FileReader: Reads File/Blob contents asynchronously.
- FileReaderSync: A synchronous reader, but only available in Web Workers.
There are also handy URL helpers:
- URL.createObjectURL() to create a temporary URL for a File or Blob
- URL.revokeObjectURL() to release it when you’re done
How it relates to other file APIs
The File API is the most basic of the web’s file-related APIs: it focuses on reading and processing data the user provides via input or drag-and-drop, including handling binary data via blobs.
- File and Directory Entries API: Works with user-provided directories/multiple files (historically Chrome-focused, often webkit-prefixed).
- File System API: Provides an origin-private virtual filesystem (OPFS) for persistent storage, and can be extended by the File System Access API to read/write user files with permission.
File API Demo
File API Overview
Select a file or drag-and-drop to inspect metadata and read content.
No files chosen
📂 Drag & Drop files here
File Details & Content:
Waiting for interaction...
:root {
--bg-color: #1e1e1e;
--text-color: #e0e0e0;
--card-bg: #2d2d2d;
--primary: #4caf50;
--accent: #2196f3;
--border: #444;
}
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: var(--bg-color);
color: var(--text-color);
display: flex;
justify-content: center;
padding: 2rem;
line-height: 1.6;
}
.container {
max-width: 700px;
width: 100%;
}
/* Hide standard input, style label as button */
.btn {
display: inline-block;
padding: 10px 20px;
background-color: var(--primary);
color: white;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
transition: opacity 0.2s;
}
.btn:hover {
opacity: 0.9;
}
/* Drag and Drop Zone */
.drop-zone {
margin: 20px 0;
border: 2px dashed var(--border);
border-radius: 8px;
padding: 40px;
text-align: center;
background-color: var(--card-bg);
transition: border-color 0.3s, background-color 0.3s;
}
/* Class added via JS when dragging over */
.drop-zone.hover {
border-color: var(--accent);
background-color: #2196f320;
/* Transparent blue */
}
/* Output Area */
.output-area {
margin-top: 20px;
padding: 15px;
background-color: #000;
border-radius: 4px;
border: 1px solid var(--border);
}
#output {
white-space: pre-wrap;
font-family: 'Consolas', monospace;
color: #a5d6a7;
}
img.preview-thumb {
max-width: 150px;
margin-top: 10px;
border: 1px solid #fff;
}
/**
* Spec Steppin' Series: File API Overview
* Source: https://developer.mozilla.org/en-US/docs/Web/API/File_API
*/
const fileInput = document.querySelector('#file-input');
const fileCountSpan = document.querySelector('#file-count');
const dropZone = document.querySelector('#drop-zone');
const output = document.querySelector('#output');
const previewContainer = document.querySelector('#preview-container');
// Helper: Format bytes to human-readable string
const formatFileSize = (bytes) => {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
// Core Function: Process the FileList object
const handleFiles = (files) => {
output.textContent = ''; // Clear previous log
previewContainer.replaceChildren(); // Clear previews
fileCountSpan.textContent = `${files.length} file(s) selected`;
// The File API gives us a FileList, which is array-like but not an array.
// We can convert it or loop standardly.
Array.from(files).forEach((file) => {
logMetadata(file);
readFileContent(file);
});
};
// 1. Inspect Metadata (Synchronous)
const logMetadata = (file) => {
const logEntry = `
--- File Metadata ---
Name: ${file.name}
Type: ${file.type || 'Unknown'}
Size: ${formatFileSize(file.size)}
Last Modified: ${new Date(file.lastModified).toLocaleString()}
---------------------
`;
output.textContent += logEntry;
};
// 2. Read Content (Asynchronous via FileReader)
const readFileContent = (file) => {
const reader = new FileReader();
// Setup Event Listeners BEFORE reading
reader.onload = (event) => {
// Success handler
const result = event.target.result;
// Logic to display content based on type
if (file.type.startsWith('image/')) {
const img = document.createElement('img');
img.src = result; // The result is a Data URL (base64)
img.classList.add('preview-thumb');
previewContainer.appendChild(img);
output.textContent += `\n[Image Preview Generated]\n`;
} else if (
file.type.startsWith('text/') ||
file.type === 'application/json'
) {
// Truncate text for demo purposes if it's huge
const previewText =
result.length > 200 ? result.substring(0, 200) + '...' : result;
output.textContent += `\nContent Preview:\n${previewText}\n`;
}
};
reader.onerror = (error) => {
console.error('Error reading file:', error);
output.textContent += `\nError reading file: ${error.message}\n`;
};
// Trigger the read operation
if (file.type.startsWith('image/')) {
reader.readAsDataURL(file); // Great for previews
} else {
reader.readAsText(file); // Great for CSV, JSON, TXT
}
};
// --- Event Listeners ---
// A. Standard Input Change
fileInput.addEventListener('change', (e) => {
handleFiles(e.target.files);
});
// B. Drag and Drop "Gotchas"
// You MUST preventDefault on 'dragover' and 'drop' or the browser
// will open the file in the tab instead of letting JS handle it.
dropZone.addEventListener('dragover', (e) => {
e.preventDefault();
dropZone.classList.add('hover'); // Visual cue
});
dropZone.addEventListener('dragleave', () => {
dropZone.classList.remove('hover');
});
dropZone.addEventListener('drop', (e) => {
e.preventDefault();
dropZone.classList.remove('hover');
// Access the files via the dataTransfer object
const files = e.dataTransfer.files;
if (files.length > 0) {
handleFiles(files);
}
});
Takeaway
If your app needs to import or preview user-selected files in the browser, the File API is where you start: FileList → File/Blob → read contents asynchronously (or synchronously in workers). It’s simple, widely useful, and the base layer for many “bring your own file” experiences on the web.

