Skip to content

Commit

Permalink
Let browser handle downloads directly - part 1(2)
Browse files Browse the repository at this point in the history
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 <[email protected]>
  • Loading branch information
ZNikke committed Dec 5, 2023
1 parent ce970d6 commit d7b5d8b
Showing 1 changed file with 51 additions and 23 deletions.
74 changes: 51 additions & 23 deletions src/scripts/dv.js
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,14 @@
.set(`items.${itemIndex}.currentQos`, status);
vf.shadowRoot.querySelector('#feList').notifyPath(`items.${itemIndex}.currentQos`);
}
/* Initiate browser file download */
function _downloadFile(url)
{
var dl = document.createElement("a");
dl.setAttribute('href', url);
dl.setAttribute('download', '');
dl.click();
}

window.addEventListener('qos-in-transition', function(event) {
updateFeListAndMetaDataDrawer([`${event.detail.options.targetQos}`], event.detail.options.itemIndex);
Expand Down Expand Up @@ -632,32 +640,52 @@
app.ls(e.detail.file.filePath, auth);
Polymer.dom.flush();
} else {
//Download the file
const worker = new Worker('./scripts/tasks/download-task.js');
// Download the file
const fileURL = getFileWebDavUrl(e.detail.file.filePath, "read")[0];
worker.addEventListener('message', (response) => {
worker.terminate();
const windowUrl = window.URL || window.webkitURL;
const url = windowUrl.createObjectURL(response.data);
const link = app.$.download;
link.href = url;
link.download = e.detail.file.fileMetaData.fileName;
link.click();
windowUrl.revokeObjectURL(url);

}, false);
worker.addEventListener('error', (err)=> {
worker.terminate();
openToast(`${err.message}`);
}, false);
worker.postMessage({
'url' : fileURL,
'mime' : e.detail.file.fileMetaData.fileMimeType,
'upauth' : app.getAuthValue(auth),
'return': 'blob'
});
let authval = app.getAuthValue();
if (e.detail.file.macaroon) {
// Unconditionally use existing macaroon if available
let u = new URL(fileURL);
u.searchParams.append('authz', e.detail.file.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 {
/*
* We don't seem to be able to pass our current auth
* via a standard method that triggers the browser standard
* file-download handling, so need to create a short-lived
* Macaroon for it.
*/
const macaroonWorker = new Worker('./scripts/tasks/macaroon-request-task.js');
macaroonWorker.addEventListener('message', (e) => {
macaroonWorker.terminate();
_downloadFile(e.data.uri.targetWithMacaroon);
}, false);
macaroonWorker.addEventListener('error', (e) => {
macaroonWorker.terminate();
// FIXME: Display an error dialog somehow
console.error(e);
}, false);
macaroonWorker.postMessage({
"url": fileURL,
"body": {
"caveats": ["activity:DOWNLOAD"],
"validity": "PT1M"
},
'upauth' : authval,
});
}
}
});

window.addEventListener('dv-namespace-open-subcontextmenu', e => app.subContextMenu(e));
window.addEventListener('dv-namespace-close-subcontextmenu', () => {
app.$.centralSubContextMenu.close();
Expand Down

0 comments on commit d7b5d8b

Please sign in to comment.