A small feature request: "I have 80 invoice PDFs called invoice (12).pdf. Can your tool rename them to 2026-10-15_Acme_550.00.pdf based on what's inside?" Reading the PDF and extracting the date, vendor and amount is the fun part. Actually renaming the files on the user's disk turned out to be the tricky part. Here are the three approaches I tried.
Option 1: File System Access API
Chromium browsers let you ask for a directory handle and write to it:
const dir = await window.showDirectoryPicker({ mode: 'readwrite' }); for await (const [name, handle] of dir.entries()) { if (handle.kind !== 'file' || !name.endsWith('.pdf')) continue; const file = await handle.getFile(); const newName = await buildName(file); if (handle.move) await handle.move(newName); // in-place rename } When it works, it's magical: files are renamed in place, no downloads. The catches:
- Availability.
showDirectoryPickeris Chromium-only. Firefox and Safari don't ship it. -
move()support for user-picked files arrived later than the rest of the API. Feature-detect it; the fallback is "write a new file withgetFileHandle(newName, { create: true }), copy the bytes, thenremoveEntry(oldName)", which is slower and not atomic. - Permission prompts. The user gets a scary "let site edit files?" dialog. Explain before the prompt why you need it.
- Collisions. Two invoices from the same vendor on the same day with the same amount happen more than you'd think. Check for existing names and append
-2.
Option 2: download each renamed file
The universal fallback is to create an object URL per file and click a hidden <a download="newName">. It works everywhere, but:
- browsers throttle or block multiple automatic downloads after the first few;
- the user ends up with 80 copies in Downloads, and the originals untouched;
- on mobile it's unusable.
I keep it only for one or two files.
Option 3: one ZIP with the renamed files
For batches, I settled on generating a ZIP in the browser (a streaming zip writer, STORE mode since PDFs are already compressed) and offering a single download. Renaming is just choosing the entry name. Benefits:
- works in every browser, including iOS Safari;
- one download prompt;
- the user can review the result before replacing anything;
- I can add a
rename-log.csvmapping old names to new names, which accountants love.
Memory-wise, stream entries from file.stream() into the zip writer instead of reading all files into memory first.
What I ship
- Chromium desktop: offer "rename in folder" (File System Access) and ZIP.
- Everything else: ZIP only, no feature-gap apology dialog, just the button that works.
- Always: a preview table of old → new names, editable, before anything is written.
The preview table was the single most appreciated part. Extraction is never 100% right, and letting people fix one cell is better than any heuristic.
Nibbo's invoice renamer takes the ZIP route: it reads the PDFs, fills an editable table of new names, and gives you the renamed files as a ZIP, with the PDFs never leaving the machine. That matters a lot when the files are other companies' invoices.
— Vitalii
