The essentials
Quick reference
One focused task per row. Jump to the related section for complete, working examples.
| Use | Syntax | Examples |
|---|---|---|
| Choose one file | <input id="avatar" name="avatar" type="file"> | View examples |
| Hint accepted formats | <input type="file" accept="image/png,image/jpeg,.webp"> | View examples |
| Choose several files | <input type="file" name="attachments" multiple> | View examples |
| Require a selection | <input type="file" name="document" required> | View examples |
| Access selected files | const files = input.files | View examples |
| Inspect file metadata | const { name, size, type, lastModified } = file | View examples |
| Check a byte limit | if (file.size > 5 * 1024 * 1024) reject(file) | View examples |
| Read text with a promise | const text = await file.text() | View examples |
| Read binary with FileReader | reader.readAsArrayBuffer(file) | View examples |
| Cancel a FileReader read | reader.abort() | View examples |
| Create a local preview URL | const url = URL.createObjectURL(file) | View examples |
| Release a preview URL | URL.revokeObjectURL(url) | View examples |
| Submit multipart form data | <form method="post" enctype="multipart/form-data"> | View examples |
| Build an upload body | formData.append('attachment', file, file.name) | View examples |
A file input gives a page access only to files the user deliberately selects. The resulting File objects expose blob data plus name and modification metadata, but names, extensions, sizes, and reported MIME types are untrusted. Use browser-side checks for fast feedback, read only what the interface needs, release temporary object URLs, and repeat every upload limit and content check on the server.
Step by step
Detailed examples
Start with a labeled native file input
input type=file invokes a user-controlled picker and represents its selected FileList. Give it a visible label and name, add multiple only when the workflow accepts several files, and use required when an empty selection is invalid. accept is a comma-separated hint made of MIME types, broad audio/video/image groups, or dot-prefixed extensions; it helps users filter choices but does not validate the result.
<label for="avatar">Profile image</label>
<input id="avatar" name="avatar" type="file"
accept="image/png,image/jpeg,.webp"
aria-describedby="avatar-help" required>
<p id="avatar-help">PNG, JPEG, or WebP; at most 2 MB.</p> Note: Do not hide the native input in a way that removes it from keyboard or accessibility APIs. A label can provide custom presentation while the input remains operable.
<label for="attachments">Attachments</label>
<input id="attachments" name="attachments" type="file" multiple> Inspect File objects and give early feedback
On input or change, input.files contains zero or more File objects. A File is an immutable Blob with name and lastModified metadata; size is measured in bytes and type is a lower-case MIME string or an empty string when unknown. Check count, size, reported type, and extension for user feedback, but consider all of them attacker-controlled and repeat authoritative content inspection on the server.
const input = document.querySelector('#avatar');
const message = document.querySelector('#avatar-message');
const allowedTypes = new Set(['image/png', 'image/jpeg', 'image/webp']);
const maxBytes = 2 * 1024 * 1024;
input.addEventListener('change', () => {
const [file] = input.files;
if (!file) { message.textContent = 'No file selected.'; return; }
if (!allowedTypes.has(file.type) || file.size > maxBytes) {
input.value = '';
message.textContent = 'Choose a PNG, JPEG, or WebP image no larger than 2 MB.';
return;
}
message.textContent = `${file.name} — ${file.size.toLocaleString()} bytes`;
}); Note: Clearing input.value is allowed; scripts cannot set it to a local filename. File.type can be empty or incorrect, so it is not a security boundary.
const selected = Array.from(input.files);
const totalBytes = selected.reduce((sum, file) => sum + file.size, 0); Choose the smallest appropriate read
Blob.text() and Blob.arrayBuffer() return promises and are convenient when the complete file is reasonably small. FileReader offers event-driven text, data-URL, and ArrayBuffer reads plus progress and abort events. Both styles can allocate memory proportional to the data read, so enforce size limits before reading large files; for incremental processing, consume file.stream() instead.
async function showText(file, output) {
if (file.size > 256 * 1024) throw new Error('Text file is too large to preview');
const text = await file.text();
output.textContent = text.slice(0, 10_000);
} Note: Blob.text() always decodes as UTF-8. Use a TextDecoder with an ArrayBuffer when the workflow explicitly supports another encoding.
function readBytes(file, signal) {
return new Promise((resolve, reject) => {
if (signal?.aborted) {
reject(new DOMException('Read aborted', 'AbortError'));
return;
}
const reader = new FileReader();
const abort = () => reader.readyState === FileReader.LOADING && reader.abort();
signal?.addEventListener('abort', abort, { once: true });
reader.addEventListener('load', () => resolve(reader.result));
reader.addEventListener('error', () => reject(reader.error));
reader.addEventListener('abort', () => reject(new DOMException('Read aborted', 'AbortError')));
reader.addEventListener('loadend', () => signal?.removeEventListener('abort', abort));
reader.readAsArrayBuffer(file);
});
} Preview blobs without base64 expansion
URL.createObjectURL(file) creates a blob URL that can be assigned to src or href without first copying the whole file into a base64 data URL. Each call creates an entry that can live for the document's lifetime, so retain the string and revoke it when replacing or removing the preview. Revoke only after consumers have started or finished loading it; dereferencing a revoked URL produces a network error.
const preview = document.querySelector('#avatar-preview');
let previewURL;
function showImage(file) {
if (previewURL) URL.revokeObjectURL(previewURL);
previewURL = URL.createObjectURL(file);
preview.src = previewURL;
preview.hidden = false;
}
window.addEventListener('pagehide', () => {
if (previewURL) URL.revokeObjectURL(previewURL);
}); Note: Decode errors still need handling. Do not treat successful image rendering as proof that a later upload is safe to store or serve.
Upload with multipart data and verify on the server
A native form that uploads files needs method=post and enctype=multipart/form-data. FormData provides the same multipart shape to fetch; let the browser generate its Content-Type boundary instead of setting that header manually. The server must independently enforce authentication, authorization, field names, count, byte limits, actual content type, filename policy, storage location, and malware or decompression safeguards appropriate to the application.
<form action="/profile/photo" method="post" enctype="multipart/form-data">
<label for="photo">Profile photo</label>
<input id="photo" name="photo" type="file" accept="image/png,image/jpeg" required>
<button type="submit">Upload photo</button>
</form> const data = new FormData();
for (const file of input.files) data.append('attachments', file, file.name);
const response = await fetch('/api/attachments', {
method: 'POST',
body: data,
credentials: 'same-origin'
});
if (!response.ok) throw new Error(`Upload failed: ${response.status}`); Note: Do not set Content-Type when sending FormData; the browser adds multipart/form-data with the required boundary. Client validation improves UX but never replaces server validation.
Local code tester
Try a local image preview
Choose a local PNG, JPEG, or WebP image up to 2 MB. The sandbox previews it with a revocable object URL and never uploads it.
Sources and further reading
References
Authoritative documentation used to verify and expand this cheat sheet.
Help us improve
Found a typo or missing example?
Tell us what would make this cheat sheet clearer, more complete, or more useful.



