File API & FileReader
Read user-selected files in the browser using the File API and FileReader preview images, parse text files, and handle binary data before uploading.
Overview
Key Points
- •`input.files` is a `FileList`; individual files are `File` objects (a subtype of `Blob`)
- •`file.name`, `file.size`, `file.type`, `file.lastModified` are the key File properties
- •`URL.createObjectURL(file)` fastest way to display/play a file; revoke with `URL.revokeObjectURL(url)` when done
- •`file.text()` returns a Promise<string> for text files (modern, preferred over FileReader)
- •`file.arrayBuffer()` returns Promise<ArrayBuffer> for binary files
- •`FileReader` legacy event-based API; use `.readAsDataURL()`, `.readAsText()`, `.readAsArrayBuffer()`
- •`input.accept` filters file types in the OS dialog (e.g., `accept="image/*"` or `accept=".pdf,.docx"`)
Examples
Image preview before upload using URL.createObjectURL
<input type="file" id="avatar-input" accept="image/*">
<img id="preview" alt="Preview" style="max-width:300px; display:none">
<script>
document.getElementById('avatar-input').addEventListener('change', (event) => {
const file = event.target.files[0];
if (!file) return;
// Validate type and size
if (!file.type.startsWith('image/')) {
alert('Please select an image file.');
return;
}
if (file.size > 5 * 1024 * 1024) {
alert('Image must be smaller than 5 MB.');
return;
}
const preview = document.getElementById('preview');
const oldUrl = preview.src;
// Create a temporary URL and show the preview
preview.src = URL.createObjectURL(file);
preview.style.display = 'block';
// Free memory when the new URL is loaded
preview.onload = () => URL.revokeObjectURL(oldUrl);
});
</script>Always revoke object URLs when you no longer need them to free memory.
Reading a text or CSV file with the modern Promise API
<input type="file" id="csv-input" accept=".csv,text/csv">
<pre id="output"></pre>
<script>
document.getElementById('csv-input').addEventListener('change', async (event) => {
const file = event.target.files[0];
if (!file) return;
try {
// Modern Promise-based read no FileReader needed
const text = await file.text();
const rows = text.split('\n').map(row => row.split(','));
document.getElementById('output').textContent =
rows.map(r => r.join(' | ')).join('\n');
} catch (err) {
console.error('Failed to read file:', err);
}
});
</script>file.text() is much cleaner than FileReader use it for any text format (CSV, JSON, XML, markdown).
FileReader for legacy environments or progress events
<input type="file" id="legacy-input">
<progress id="progress" value="0" max="100" style="display:none"></progress>
<div id="result"></div>
<script>
document.getElementById('legacy-input').addEventListener('change', (event) => {
const file = event.target.files[0];
if (!file) return;
const reader = new FileReader();
const progressBar = document.getElementById('progress');
progressBar.style.display = 'block';
reader.onprogress = (e) => {
if (e.lengthComputable) {
progressBar.value = (e.loaded / e.total) * 100;
}
};
reader.onload = (e) => {
document.getElementById('result').textContent = e.target.result;
progressBar.style.display = 'none';
};
reader.onerror = () => console.error('Read failed');
reader.readAsText(file);
});
</script>FileReader is useful when you need progress events for large files.
Common Mistakes
- Not revoking object URLs with URL.revokeObjectURL() each URL holds a reference to the file in memory until revoked or the page unloads
- Trusting file.type for security validation MIME type comes from the OS and can be spoofed. Validate on the server too
- Using FileReader for simple text/JSON parsing file.text() and file.arrayBuffer() are simpler and Promise-based
Note
For multiple file selection, add the `multiple` attribute to the input and iterate over `input.files` with a `for...of` loop. Each file is processed independently.
Exercises
Write HTML to solve each exercise. Use Preview to see it render in a live iframe, then mark as done.
Completed: 0 / 5 exercises
Knowledge Check
Question 1 of 4