Let browser handle downloads directly and support multi-file download#294
Let browser handle downloads directly and support multi-file download#294ZNikke wants to merge 6 commits intodCache:masterfrom
Conversation
|
Ugh, discovered that I confused myself when testing to think that it worked in the shared-files view. It doesn't, in the sense that the shared-files view uses another code path with similar functionality as the main one and thus still has the old behavior. I'll look into making this code be used there as well, to match the pull request description. |
757a1fb to
bb332f3
Compare
|
There, I think this works as intended now. |
Currently dCacheView handles downloads by first doing the download and only when the download has completed it passes the object along for the browser to handle. The result is no feedback at all for users when initiating download of large files since the save dialog is shown on completion, and huge files might not download at all if the temporary browser download location runs out of space. Work around this by letting the browser handle the download instead, the method chosen is to create a temporary Anchor element to drive the download action as it has an explicit download attribute and avoids issues with modern browser pop-up blockers. Since username/password (Basic) auth can't be reliably passed along a short-lived Macaroon is created to handle the download. Existing Macaroons are used as-is to handle download in the shared-files top-level view. Sessions with certificate authentication are assumed to work as-is, bypassing the Macaroon generation stage. The result is a decent end user experience when downloading files, including huge files that are common in a scientific data store. Missing in this patch is handling of subdirectories in the shared-files view. Credits go to various threads on https://stackoverflow.com/ for explaining numerous corner cases and nuances in this area. Fixes: dCache#269 Signed-off-by: Niklas Edmundsson <nikke@hpc2n.umu.se>
dCacheView is limited to file-by-file downloads, which is frustrating users since there is support for multi-file selection. This patch builds on the previous one to enable downloads for multi-file selections. The simplest solution is implemented that triggers multiple file downloads in the browser, this is also found to work best with tablets and smartphones, where the traditional method by handling multi-file download by providing a .zip archive of the files is really cumbersome to handle. There is also a completion of the implementation to handle sub-directories in the shared-files view. The end result is that it is now possible to select multiple files in a directory and then download them in a smooth manner. Fixes: dCache#268 Signed-off-by: Niklas Edmundsson <nikke@hpc2n.umu.se>
bb332f3 to
17161f8
Compare
There was a problem hiding this comment.
Pull request overview
This PR changes dCacheView’s download flow to rely on the browser’s native download handling (instead of downloading into a Blob first), and adds support for downloading multiple selected files by initiating multiple direct downloads. It introduces short-lived macaroons for authenticated downloads where the browser cannot reliably replay the current auth.
Changes:
- Add a new download initiation path (
app._initiateDownload) that triggers a native browser download via a temporary<a download>link, optionally appending/obtaining a macaroon. - Enable “Download” in the multi-selection context menu and initiate multi-file downloads.
- Reuse the same download initiation logic for the shared-files page.
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 6 comments.
| File | Description |
|---|---|
| src/scripts/dv.js | Adds native-download helpers, macaroon-based download initiation, and multi-file download triggering. |
| src/elements/dv-elements/contextual-content/namespace-contextual-content.html | Enables download action for multi-selection in the context menu. |
| src/elements/dv-elements/file-sharing/shared-files-page/shared-files-page.js | Switches shared-file downloads to use the new central download initiation logic. |
| src/elements/dv-elements/files-viewer/files-viewer.html | Minor formatting change (closing tag alignment). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } else if (this.multipleSelection) { | ||
| this.$.delete.addEventListener('tap', this._delete.bind(this)); | ||
| this.$.move.addEventListener('tap', this._move.bind(this)); | ||
| // FIXME: Is this the place to figure out if any | ||
| // directories are selected and disable download if | ||
| // that's the case? | ||
| this.$.download.addEventListener('tap', this._openOrDownload.bind(this)); | ||
| } else if (this.currentDir) { |
| const fileURL = getFileWebDavUrl(file.filePath, "read")[0]; | ||
| let authval = app.getAuthValue(); | ||
|
|
||
| // Unconditionally use existing macaroon if available | ||
| let macaroon = undefined; | ||
| if (file.macaroon) { | ||
| macaroon = file.macaroon; | ||
| } | ||
| else if(file.authenticationParameters !== undefined && file.authenticationParameters.scheme === "Bearer") { | ||
| macaroon = file.authenticationParameters.value; | ||
| } | ||
|
|
||
| if(macaroon !== undefined) { | ||
| let u = new URL(fileURL); | ||
| u.searchParams.append('authz', macaroon); | ||
| _downloadFile(u); | ||
| } | ||
| else if(!authval) { | ||
| /* | ||
| * No explicit auth, so using cert auth, which means we can | ||
| * just access the file directly without having the user | ||
| * re-login. | ||
| */ | ||
| _downloadFile(fileURL); | ||
| } | ||
| else { |
| /* Need to stagger starts to allow browser to start | ||
| * this download before initiating the next one. | ||
| */ | ||
| setTimeout(app._initiateDownload, i*1000, toDL[i]); |
There was a problem hiding this comment.
The initial reason for the delay was browsers simply refusing to trigger multiple downloads otherwise. This might well have changed, but this needs to be carefully tested using multiple browsers/OS:es. Testing the lower hanging fruits first.
| }, false); | ||
| macaroonWorker.addEventListener('error', (e) => { | ||
| macaroonWorker.terminate(); | ||
| // FIXME: Display an error dialog somehow |
There was a problem hiding this comment.
This error dialog is better than a FIXME, hard to test so hope it works...
| 'upauth' : this.getAuthValue(), | ||
| 'return': 'blob' | ||
| }); | ||
| app._initiateDownload(e.detail.file); |
|
I'll work through the suggested changes, most of it seems to make sense at a glance. |
…ones Copilot review suggested change. Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot review suggested change.
Copilot review suggested change.
Delaying download activation loses the user-activation gesture, this can trigger browser to block actions as non-user initiated unwanted actions. Copilot review suggested change.
Currently dCacheView handles downloads by first doing the download and only when the download has completed it passes the object along for the browser to handle. The result is no feedback at all for users when initiating download of large files since the save dialog is shown on completion, and huge files might not download at all if the temporary browser download location runs out of space. Further frustrating users, you can select multiple files but is not allowed to select download.
Work around this by letting the browser handle the download instead, the method chosen is to create a temporary Anchor element to drive the download action as it has an explicit download attribute and avoids issues with modern browser pop-up blockers. Since username/password (Basic) auth can't be reliably passed along a short-lived Macaroon is created to handle the download. Existing Macaroons are used as-is to handle download in the shared-files view. Sessions with certificate authentication are assumed to work as-is, bypassing the Macaroon generation stage. The same basic mechanism is used to allow download of multiple files, this is also found to be easier to use on tablets/smartphones compared to the common .zip archive solution.
The result is a decent end user experience when downloading files, including multiple huge files that are common in a scientific data store.
Credits go to various threads on https://stackoverflow.com/ for explaining numerous corner cases and nuances in this area.
Fixes: #269
Fixes: #268