How the browser delivers a folder
Two mechanisms: the directory picker (<input type="file" webkitdirectory>, every file carries a webkitRelativePath) and drag-and-drop traversal (webkitGetAsEntry() walks the dropped folder recursively; each entry has a fullPath). Both exist only at selection time — the upload request itself carries just a file name, which is where most stacks lose the tree.
The AjaxUploader flow
The control captures the relative path at selection time and transmits it with each upload. Server-side it is sanitized (traversal segments removed, invalid characters neutralized) and stored with the upload, then surfaced on the event you already handle:
<au:AjaxFileUpload ID="Uploader1" runat="server"
AllowMultiple="true"
AutoPostBack="true"
OnFileUploaded="Uploader1_FileUploaded" />
protected void Uploader1_FileUploaded(object sender, FileUploadedEventArgs e)
{
// e.RelativePath: "photos/2026/beach.jpg" for folder uploads, null for plain files.
// Already traversal-sanitized - still combine it under a root YOU choose.
var relative = e.RelativePath ?? Path.GetFileName(e.FileName ?? "file");
var destination = Path.Combine(Server.MapPath("~/App_Data/uploads"),
relative.Replace('/', '\\'));
Directory.CreateDirectory(Path.GetDirectoryName(destination));
new UploadService().CopyFile(e.FileGuid, destination);
}
On the client, task.relativePath is available in every callback (see the Folder Drop demo, which lists each file's preserved path as it completes).
Why sanitizing is not optional
A client-supplied path is attacker-controlled text: without cleaning, ..\..\web.config is a perfectly valid "relative path". AjaxUploader removes ./.. segments, normalizes separators, neutralizes drive-letter colons and invalid characters, and drops oversized input — before the value is ever stored. Your code still decides the root folder; the sanitized path can only ever descend from it.
Details worth knowing
e.RelativePath is null for plain (non-folder) selections — a bare file name carries no structure, so nothing is invented.
- Chunked uploads preserve the path too; it travels in the chunk-complete request.
- Client-side image transforms (resize, compress, watermark) keep the path across the new file they produce.
- Requires AjaxUploader.dll File version 5.3.0.808 or later (right-click → Properties → Details).