JavaScript File API Table

PieceWhat it doesField note
input.files / dataTransfer.filesGet File objectsFile = Blob + name/lastModified - the metadata arrives free
file.text()Read as text, promiseThe modern one-liner - no FileReader event dance
file.arrayBuffer()Read as bytesFeeds typed arrays, hashing, uploads - the binary path
FileReader.readAsDataURLBase64 embedSmall previews only - 33% size tax on every byte
createObjectURL(file)Zero-copy referenceA URL POINTING at the file - revokeObjectURL or leak memory
file.slice(start, end)Partial readsBig file chunking - read the header, hash in pieces
type & size BEFORE readValidate firstMIME is client-claimed - the server re-checks everything
showOpenFilePickerThe newer handle APIRe-readable handles without re-prompting - check support
Reference: the MDN File API reference. File objects are Blobs with a name: they arrive from input and drag-drop carrying metadata (size, type, lastModified) readable BEFORE any content is loaded - validate first, then choose the read shape. The modern reads are PROMISES (file.text(), file.arrayBuffer()), replacing the FileReader event dance for everything except progress bars. Bottom line: createObjectURL is the zero-copy preview path (its URL stays alive - revokeObjectURL on cleanup or leak), slice() enables chunked hashing of gigabyte files, and the client-side MIME type is a claim, not a fact. Related tools: typed arrays table (what arrayBuffer() hands you), drag and drop table (the other way files arrive), and FormData table (the upload path).

File objects are Blobs with a name: they arrive from an input or a drop carrying size, type and lastModified readable BEFORE any content loads - validation is free, reading is a choice. The modern reads are promise one-liners: file.text() and file.arrayBuffer() replaced the FileReader event dance.

Bottom line: createObjectURL(file) is the zero-copy preview path - it hands you a URL that POINTS at the file's bytes without reading them, which is also why it leaks until you call revokeObjectURL. And the client-side MIME type is a claim, not a fact: the server re-checks everything.

The honest part: FileReader is not dead - it owns progress events (the upload/parse progress bar) and readAsDataURL for tiny inline previews. But its callback-and-event shape loses to promises everywhere else, and the 33% base64 size tax on dataURLs makes them wrong for anything over an icon.

How to use

  1. Validate before reading: check file.size and file.type against limits first - metadata is free, and a 4GB surprise should stop before the read starts.
  2. Read with promises: const text = await file.text() for config and logs; const buf = await file.arrayBuffer() for hashing, parsing or typed arrays.
  3. Preview with object URLs: const url = URL.createObjectURL(file); img.src = url; then URL.revokeObjectURL(url) after load - zero-copy, unlike the base64 detour.

Frequently asked questions

When is file.text() better than FileReader.readAsText?

Almost always in modern code: both produce the file's text, but text() is a promise that composes with async/await and error handling, while FileReader demands load/error/progress event wiring in callback style. FileReader keeps two genuine niches: progress events (a reading-progress bar over a large file - promises report only completion) and readAsDataURL, which no File method replicates. The refactoring rule: new code uses text()/arrayBuffer() by default and reaches for FileReader only when the progress bar or the data URL is the actual feature.

Why does createObjectURL need revokeObjectURL, and when exactly do I call it?

The URL holds a live reference. createObjectURL hands out a blob: URL that keeps the file's bytes alive in memory as long as the URL exists - which is the point (the img can load it lazily) and the leak (nothing auto-revokes; a single-page app creating URLs per upload accumulates every file ever selected until reload). The pattern: revoke after the consumer finishes - img.onload is the classic moment for previews, component teardown for longer-lived usage. Data URLs, by contrast, are self-contained strings (no revoke needed) at a 33% size tax - which is why the split is: object URLs for real files, data URLs for tiny inline embeds.

How does file.slice() enable handling gigabyte files?

Blobs support partial views without loading: file.slice(0, 1024) is a new Blob over those bytes, read on demand. Chunked processing follows - read 8MB pieces in a loop, hash each piece incrementally (resumable uploads are built exactly this way: per-chunk checksums, retry only failed chunks), or parse just a file header to identify the type before committing to the rest. Memory stays bounded because only one chunk is in RAM at a time. The same slice powers Range-style uploads and the difference between a tool that handles a 5GB video and one that dies on it.

Why is checking file.type not a security measure?

Because the client controls the claim. The MIME type arrives from the browser based on the file's extension and OS registration - renaming virus.exe to report.pdf yields type 'application/pdf' - so client-side type checks are UX (friendly early errors), not security. The real checks live server-side: inspect magic bytes (PDF starts with %PDF, PNG with its signature), re-scan content, and serve uploads from a separate origin with Content-Disposition so stored payloads cannot execute in your origin. Client validation's honest role: stop honest users from honest mistakes, fast - before the multi-megabyte upload starts.

Related tools