Upload folder HTML: preserve relative paths before transfer

Two report.txt files have the same size but different folders. Deduplicating by filename and size drops one before any upload begins.

Preserve the relative path with each selected file and keep transfer status separate.

The example retains three distinct paths totaling ten bytes in the authored Notes fixture. Notes/a/report.txt contains AAA, Notes/b/report.txt contains BBB, and Notes/readme.txt contains READ. The two reports stay separate despite matching names and sizes. You can inspect that fixture, a duplicate-path conflict and a flat replacement, or use local picker controls; nothing transfers to a server.

Keep both report.txt files.
Keep both report.txt files.
Open the full example ↗

Capture the path with the file

A directory input using webkitdirectory returns file records with relative paths reflecting the selected hierarchy, subject to actual browser support. Capture each File reference with its original relative path, basename, size, selection-operation ID and row ID. Do not store only the basename and attempt to reconstruct folders later. Preserve spelling and case. Notes/a/report.txt and Notes/b/report.txt are different identities in one selection even though both are three bytes and named report.txt.

The fixture's three contents are literal ASCII without newline or byte-order mark: 3+3+4=10 bytes. A basename-plus-size deduplication would incorrectly reduce it to two files and seven bytes. Do not substitute content equality for this path-preservation task either; separate paths remain separate records. The sample buttons load authored metadata, clearly labeled as fixture data rather than files actually selected from a user's disk. Actual picker metadata should remain distinguishable from that worked case.

Hold collisions without overwriting records

If directory mode supplies the exact same relative path twice, preserve both rows and hold the manifest as conflicting. A missing relative path also needs an explicit reason rather than an invented parent folder. Flat selection uses operation-scoped row identities because hierarchy is unavailable; same-name records remain separate without fabricated folder paths. Any future server must treat received paths as untrusted and apply its own destination rules. This local page assigns no server path and performs no filesystem writes or storage operation.

FileList describes files, not an independent inventory of empty directory objects. Parent folders can be represented from file paths, but an empty subfolder contributes no file row. Do not promise complete filesystem-tree reconstruction from this manifest. Browser/platform support and actual returned root prefixes need native testing. The alternative individual-file input is explicitly flat, not equivalent directory support, and the presence of a directory-shaped control alone cannot prove the selected hierarchy was retained correctly.

Replace the current selection

Every accepted selection replaces the previous manifest and receives a fresh operation identity. Loading the readme flat fixture after the Notes fixture therefore shows one record/four bytes, with no old report rows or inherited Notes path. Reset clears current records and native input values, retiring the operation. Cancel without a new accepted selection leaves the old current manifest under this policy. If later adding asynchronous file reads, bind completion to the captured operation so an older callback cannot repopulate a replaced selection.

Finish by comparing current paths, count and byte total before transfer. The page reads metadata only and labels transfer not started and storage not established. File references in memory and selected-byte totals are not remote receipts. Keep any later endpoint response and durable storage confirmation as separate evidence rather than turning a clean local manifest into Uploaded successfully. When testing the intake implementation, record what the browser actually selects separately from the demonstration fixtures.

Make your own working example

Build a local selected-file manifest with labeled directory input webkitdirectory/multiple and separately labeled flat multiple-file input. Retain the current manifest in page state with each actual File reference, original relative path, name, size, type, last-modified time, operation ID and operation-scoped row ID, without reading contents or sending data. Each accepted selection replaces current records. Cancel with no files retains the current manifest. Reset clears records and input values.

Provide authored-fixture buttons for Notes/a/report.txt AAA 3 bytes, Notes/b/report.txt BBB 3 bytes, Notes/readme.txt READ 4 bytes; total 3 files / 10 bytes. Keep both report records. A flat readme fixture replaces them with 1 record / 4 bytes and hierarchy unavailable. A duplicate-path fixture retains both conflicting rows and holds acceptance. Directory records missing relative paths are held; flat records use row identities and never invent paths.

Display current operation, mode, actual-versus-fixture label, paths and byte totals together. No basename-size or content deduplication. Empty directories cannot be reconstructed from FileList. State transfer not started and storage not established; no upload endpoint, filesystem writes or persistence claim. Native picker/browser support needs separate testing; fixture button tests do not count as picker evidence.

Use one self-contained HTML file with no remote dependency. Keep the initial sample, output, and labeled controls near the top. Use at least 16px text, strong contrast, visible focus, and a stacked layout at 360px without horizontal overflow. Use explicit type=button click handlers and explicit DOM selectors; do not depend on native form submission. Clear stale feedback when task inputs change. Render entered text safely. Keep practice data local to the current page; do not imply saving, sending, or production integration. Return only the finished HTML.

Review the path-preservation rule

Keep actual picker observations with the intake implementation. In an HTML manifest explanation, NOS lets you revise eligible static instructions visually without AI regeneration. Clarify why both report.txt paths must remain visible before anyone adds a transfer step.

Keep reading