File API
Once a user picks files in the browser — through an <input type="file"> or by dropping them on the page — JavaScript receives File objects. The File API is the set of types and methods around those objects: FileList, File, Blob, FileReader, and the modern file.arrayBuffer() / file.text() shortcuts. This page covers picking files, reading their contents, and handling drag-and-drop.
The input element
<input id="picker" type="file" accept="image/*" multiple />
Three useful attributes:
accept— MIME-type filter shown in the picker.image/*,application/pdf,.txtall work.multiple— let the user select more than one file.capture="environment"— on mobile, hint that you want the rear camera (withaccept="image/*"orvideo/*).
FileList and File
The picker exposes input.files — a FileList, which is array-like (length, indexed access) but not an array. Each entry is a File that extends Blob.
document.getElementById("picker").addEventListener("change", (e) => {
const files = Array.from(e.target.files); // turn FileList → Array
for (const file of files) {
console.log(file.name); // "vacation.png"
console.log(file.type); // "image/png"
console.log(file.size); // bytes
console.log(file.lastModified); // ms timestamp
}
});Reading file contents
Modern code uses the Blob methods directly — they return promises and are easier to compose than the old FileReader events.
const file = e.target.files[0]; const asText = await file.text(); // string (UTF-8) const asBytes = await file.arrayBuffer(); // ArrayBuffer const asStream = file.stream(); // ReadableStream const asJson = JSON.parse(await file.text());
FileReader — the older event-based API
Still useful in edge cases — particularly when you want a data URL for previewing an image.
const reader = new FileReader();
reader.addEventListener("load", () => {
preview.src = reader.result; // a data: URL
});
reader.addEventListener("error", () => {
console.warn("read failed");
});
reader.readAsDataURL(file);
// other modes: readAsText, readAsArrayBuffer, readAsBinaryString (legacy)Object URLs — the better preview
URL.createObjectURL(file) returns a synchronous blob: URL the browser can use anywhere a normal URL works. It is much lighter than reading the whole file into a data URL.
const url = URL.createObjectURL(file);
img.src = url;
img.addEventListener("load", () => URL.revokeObjectURL(url));Drag-and-drop uploads
Drag-and-drop integrates with the same File API. Three events are involved on the drop zone.
drop.js
const zone = document.querySelector(".dropzone");
zone.addEventListener("dragover", (e) => {
e.preventDefault(); // must call to allow drop
zone.classList.add("over");
});
zone.addEventListener("dragleave", () => zone.classList.remove("over"));
zone.addEventListener("drop", (e) => {
e.preventDefault();
zone.classList.remove("over");
const files = Array.from(e.dataTransfer.files);
upload(files);
});The preventDefault in dragover is what tells the browser this element is a drop target. Without it, the browser navigates away to open the dropped file in a new tab.
Uploading with fetch
A File is a Blob, and fetch accepts blobs as a body — directly or as part of a FormData.
single file, raw body
await fetch("/upload", {
method: "POST",
headers: { "Content-Type": file.type },
body: file,
});multipart with FormData
const data = new FormData();
for (const file of files) data.append("files", file, file.name);
await fetch("/upload", { method: "POST", body: data });Slicing big files
Blobs (and therefore Files) have a .slice(start, end, type?) method. Combined with fetch, that is how chunked uploads and pause-resume features are built.
const CHUNK = 1024 * 1024;
for (let offset = 0; offset < file.size; offset += CHUNK) {
const chunk = file.slice(offset, offset + CHUNK);
await fetch("/upload/chunk", { method: "POST", body: chunk });
}