Notes › Tech Notes › Drop The Whole Folder And The Candidate Pool Gains 1 Name: What The Random File Page Reads From A Directory

Drop The Whole Folder And The Candidate Pool Gains 1 Name: What The Random File Page Reads From A Directory

Drop A Whole Folder And The Candidate Pool Grows By 1

Drag a directory that holds 2558 files onto the drop area in the middle of the page, release the mouse, and the counter in the pool moves by exactly 1. The disk really does line up 2558 files, but only 1 name enters the pool.

This page works with names from start to finish, and the naming rule behind the Random File tool is exactly what explains the four places where folders, duplicate names, file size and the count box are easy to misread.

The Code Answers First: Six Directory APIs, 0 Hits Each

In the 459-line template, showDirectoryPicker, webkitdirectory, webkitGetAsEntry, getAsFileSystemHandle, dataTransfer.items and webkitRelativePath each hit 0 times. The only file entry point is the input at L204 that carries nothing but multiple, <input type="file">; change (L301) and drop (L299) both funnel into handleFiles (L303-311), and for every incoming entry it reads one line only, f.name (L306). Multi-select, dropping files, dropping a folder: three doors that collapse into a single implementation, and the only difference is what the browser hands over as FileList.

Hand The Folder To The Input And You Get One Empty Shell

Chrome builds a FileList of len:1 / sizeSum:0 for that L204 input, so the pool gains one shell of a name, tmp_dir_633. Dropping a directory entry whose type is an empty string in the same area also pools just 1 item, and the name stored is the folder label itself, 项目资料2026. Neither path walks below the folder into the files inside it.

One boundary has to be stated plainly: showDirectoryPicker() in a headless browser really returns SecurityError, and the message text contains Must be handling a user gesture to show a file picker; inside the same probe typeof is function and isSecureContext is true. Setting webkitdirectory = true from JS and then injecting the folder over CDP gives the same shape three times in a row: setFileInputFiles answers ok, while the captured change array stays empty and the pool stays at 0. Both readings are limits of this headless setup (not triggerable), not proof that browsers cannot read folders. So the conclusion for this page stops at code level: six directory APIs, 0 hits each, and a real user on this entry point has multi-select over files only.

How Much Goes In: No Hard Cap

One injection of 2558 real files on disk gives a pool of 2558, a preview of 35748 characters, and 413.1 ms for the whole handleFiles + updatePool chain. With a synthetic DataTransfer dispatching a real drop, every entry is pooled however large the batch grows:

EntriesTemplate-side cost
10001 ms
2000016 ms
5000065 ms

Those 50000 entries cost only 65 ms, and the price lands on readability: the preview column stretches to 74630px while its container is 120px, so just 0.16% of it is on screen.

Drop size vs template time1000 items1 ms20000 items16 ms50000 items65 msPreview 74630px vs 120px box

Fig. 1 Bar length is proportional to milliseconds.

Names Enter The Pool, Bytes Stay On Disk

.size, .type, FileReader, Blob, createObjectURL, fetch and accept all hit 0 times, so big_10mb.bin at 10485760 bytes and file_0001.txt at 33 bytes are fully equivalent once they are in the pool. The page carries no failure path for an unsupported type or an oversized file either, and the empty-state rule .rfile-empty (L160-168) is never referenced by any element. Right after the injection, L301 clears input.value, so the file handles are discarded on the spot and only the string list survives.

Inside One Folder, Names Collide

The disk sample holds 500 groups of same-named files, 2 copies in each group: the pool has 2558 slots while distinct names number only 2058, and duplicates take 1000/2558 = 39.1% of all slots. With a 2558 pool, count=1 and the switch left off, 2000 runs add up to 2795 slot-level hits even though only 2000 draws happened; the extra 795 of them, 39.8%, all land on duplicated names. A file whose name is shared wins 2 times as often as a unique one, while both the result list and the copied string keep the bare name, so nothing shows which copy came up.

What the without-replacement pass dedupes is slots, not names (L411-415): on the real 2558 pool with count=20 over 100 runs, 4 runs pulled two byte-for-byte identical file names inside the same run, and count=5 over 400 runs had 1 such run. Runs are independent of each other as well: a pool of 5 items drawn with count=2 over 3 runs showed delta.txt in both run 2 and run 3, and the whole-list overwrite at L418 means the statistic reads the row count of the last run only (L352), never a cumulative value.

Slots, names, dup ratioSlots2558Distinct names2058Dup slots1000Dup share 1000/2558 = 39.1%

Fig. 2 Bar length is proportional to item count.

The Count Box Has A min And No max

The extraction count input at L224 declares only min="1". The parseInt(v)||1 at L367 quietly turns 0 / abc / 1000 / 1e3 / 0x10 into 1, and 2.7 into 2. validity, checkValidity and reportValidity each appear 0 times across the template, and only 3 toasts can interrupt a draw: the candidate pool is empty (L363), the count has to be at least 1 (L369), and the count cannot exceed the pool size (L374). Type 100000 and the page genuinely runs: 112291 ms of wall clock, 300070 page elements, a copied string of 1397501 characters.

The randomness comes from the 3 call sites of Math.random: animation (L390), draws with replacement (L406), and the shuffle (L412). crypto, getRandomValues and seed each hit 0 times, so no reproducible sequence can be handed back. Pulling 1 item without replacement still shuffles all 2558 entries, and rfileRolling is written 2 times but read 0 times (L379, L399), so another click during the animation is not blocked.

Export Is One String Only

copyResults() (L442-445) hands UU.copy a single string: plain LF separators, no index, no header, no trailing newline. On screen the rows are numbered up to 1..7, yet the clipboard receives 7 bare names, and 1000 items serialize to 3999 characters. Branch L37 and branch L53 in app.js raise the same notice, copied to the clipboard, so even a failed fallback looks like success; download hits 0 times and there is no file export path at all.

Compare the 14 claims listed in config/tools.php L1513-1525: 9 of them are marked as conflicts, and the intro line about no repeats, each file drawn only once, is one of them, which the duplicate-name section above overturns directly.

Three Boundaries People Ask About

Q: Can This Page Load A Whole Folder?

At code level, no: the six directory APIs hit 0 times each, and both dropping a folder and handing one to the input pool a single name.

Q: Do Two Same-Named Files Count As One Candidate Or Two?

Two slots with one chance each, so the hit rate is 2 times that of a unique name, while the result still cannot say which copy came up. The expected-value arithmetic behind that was worked out in the old random wheel article; this one only states the rule.

Q: Can I Filter By Size Or Type First?

The template never reads bytes, so 10485760 and 33 are equivalent; there is neither a filter nor a weighted draw, and every hit of weight is a CSS font weight.

📎 Related articles

The Cost Of Text Diff Is All In One (m+1)×(n+1) Table: 6000 Lines Still Finish, 8000 Lines Die On Heap Out Of Memory 2026-10-28 All 100,000 Keys Land in the DOM: Render Scale, Stack-Overflow Depth and Copy Rewrites in the JSON View 2026-10-28 A RegExp With the g Flag Keeps State: 4 test() Calls on One Object Pass Only 2 2026-10-28 27760000 Pixels Identical And Size Up 15.881 Times: The Three-Layer Byte Ledger Of JPG To PNG 2026-10-28 20 Filename Lines Leave 18 Rows In 2 Columns: Pasted Into Excel They Sit In 1 Column Until Text To Columns Rewrites 1e5 As 100000 2026-10-28