Browser File API Deep Dive: Privacy-First Local File Handling

技术架构(Updated May 20, 2026)

Evolution of Browser File Handling

Early web apps relied on <input type="file"> plus server uploads. After HTML5, the File API let JavaScript read local files without uploading—a shift that enabled “browser-local tools”: PDF editing, image compression, and video transcoding entirely on the user’s device, with files never leaving the browser.


Core API Layers

File (user-selected file with name/size/type/lastModified)
  ↓ extends
Blob (binary large object with size/type)
  ↓ read as
ArrayBuffer (raw bytes—for WASM/crypto)
  ↓ or
Text (UTF-8 string—for JSON/CSV)
  ↓ or
Data URL (Base64—for inline display)
  ↓ output as
Blob → Object URL → download / preview

File vs Blob

// File is a Blob subclass with name and lastModified
const input = document.querySelector('input[type="file"]');
input.addEventListener('change', (e) => {
  const file: File = e.target.files[0];
  console.log(file.name);         // "report.pdf"
  console.log(file.size);         // 2048576 (bytes)
  console.log(file.type);         // "application/pdf"
  console.log(file.lastModified); // 1717200000000
});

Reading methods compared

Method Return type Use case Memory
file.text() string JSON, CSV, text Full load
file.arrayBuffer() ArrayBuffer PDF, images, WASM input Full load
file.stream() ReadableStream Large files, chunked Chunked read
FileReader.readAsDataURL() Data URL Image preview Base64 +33% size

ToolsKu strategy: < 50MB use arrayBuffer(), > 50MB use stream() chunking.


Object URL: Zero-Copy Preview and Download

// Temporary URL (in-memory reference, not a disk file)
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);

// Preview
iframe.src = url;

// Download
const a = document.createElement('a');
a.href = url;
a.download = 'merged.pdf';
a.click();

// ⚠️ Must revoke or memory leaks
URL.revokeObjectURL(url);

Object URLs use the blob: scheme and point at in-memory Blobs. Each unreleased URL holds memory—revoke promptly during batch processing.


Drag-and-Drop Upload

const dropZone = document.getElementById('drop-zone');

dropZone.addEventListener('dragover', (e) => {
  e.preventDefault(); // Required or the browser opens the file
  e.dataTransfer.dropEffect = 'copy';
});

dropZone.addEventListener('drop', (e) => {
  e.preventDefault();
  const files = Array.from(e.dataTransfer.files);
  const pdfs = files.filter(f => f.type === 'application/pdf');
  processFiles(pdfs);
});

DataTransfer.files returns a FileList compatible with <input type="file">’s files.


Privacy Architecture: Why Local Processing Is Safer

Traditional online tool data flow

User file → HTTPS upload → Server processing → Download result → Server delete (claimed)
                                    ↑
                              May be cached, logged, backed up

ToolsKu data flow

User file → File API read → Browser memory/WASM → Blob download
                ↑
          Never leaves the device
Dimension Server processing Browser-local
Transfer Upload + download Zero network
Privacy risk Server may retain Cleared when tab closes
Speed Network-bound CPU-bound
Size limit Often 10–50MB Device RAM
Offline No Yes (with PWA cache)

Streams API: Very Large Files

Videos over 500MB loaded entirely into ArrayBuffer can OOM:

async function processLargeFile(file: File) {
  const stream = file.stream();
  const reader = stream.getReader();
  const chunks: Uint8Array[] = [];

  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    chunks.push(value); // One chunk at a time (often 64KB)
  }

  const totalLength = chunks.reduce((sum, c) => sum + c.length, 0);
  const result = new Uint8Array(totalLength);
  let offset = 0;
  for (const chunk of chunks) {
    result.set(chunk, offset);
    offset += chunk.length;
  }
  return result;
}

FFmpeg.wasm can write Uint8Array into its virtual FS; with Streams, multi-GB video is possible when RAM allows.


File Type Detection: MIME vs Magic Bytes

File.type comes from OS extension mapping and is unreliable:

// ❌ User can rename .exe to .pdf
file.type === 'application/pdf' // May be true but not a real PDF

// ✅ Read magic bytes in header
const header = new Uint8Array(await file.slice(0, 5).arrayBuffer());
const isPdf = header[0] === 0x25 && header[1] === 0x50 &&
              header[2] === 0x44 && header[3] === 0x46; // %PDF

ToolsKu’s MIME sniffing tool uses magic bytes—more accurate than extensions.


Download Best Practices

Trigger download

function downloadBlob(blob: Blob, filename: string) {
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = filename;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
}

Batch download: ZIP

Don’t trigger many downloads in a row (browsers block pop-ups). ToolsKu uses JSZip:

PDF to images → 50 PNGs → JSZip → download images.zip

Summary

The File API is the foundation of browser-local tools. Mastering File → Blob → ArrayBuffer, Object URL lifecycle, and Streams for large files is core to privacy-first web tools. ToolsKu’s 200+ tools follow this model: no upload, local processing, cleared on close.

Try these browser-local tools — no sign-up required →

#File API#Blob#ArrayBuffer#隐私#本地处理