JavaScriptFile API

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

HTML
<input id="picker" type="file" accept="image/*" multiple />

Three useful attributes:

  • accept — MIME-type filter shown in the picker. image/*, application/pdf, .txt all work.

  • multiple — let the user select more than one file.

  • capture="environment" — on mobile, hint that you want the rear camera (with accept="image/*" or video/*).

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.

JS
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.

JS
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.

JS
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.

JS
const url = URL.createObjectURL(file);
img.src = url;

img.addEventListener("load", () => URL.revokeObjectURL(url));
Revoke when done
Each `createObjectURL` reserves memory for the file. If you forget to call `revokeObjectURL`, the data stays alive until the page unloads.
Drag-and-drop uploads

Drag-and-drop integrates with the same File API. Three events are involved on the drop zone.

drop.js

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

JS
await fetch("/upload", {
  method: "POST",
  headers: { "Content-Type": file.type },
  body: file,
});

multipart with FormData

JS
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.

JS
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 });
}
Privacy is the default
A page never sees a file until the user gives it one. There is no API to enumerate the user's disk. Anything you receive came from a deliberate pick or drop, and the browser strips the original path — you only get the filename.