我想在用户单击允许他们下载文件的超链接后将用户重定向到其他网页。但是,由于他们需要在打开/保存文件对话框中进行选择,因此我不想重定向它们,直到他们接受下载。
如何检测到他们执行了此操作?
答案 0 :(得分:6)
正如我多年来维持download.js所发现的那样,只有无法告诉来自JS(或者可能一般来说,见下文)用户选择的内容处理下载打开/保存对话框。这是一个常见的功能要求,多年来我一直在反复研究它。我可以自信地说,这是不可能的;如果有人能演示一种机械方式来确定任何文件的提示后用户操作,我会快乐地支付10倍的赏金!
此外,它并不仅仅是JS规则的问题,浏览器下载和提示此类文件的方式使问题变得复杂。这意味着即使服务器也不能总是告诉发生了什么。对于一些特定情况,可能会有一些特定的解决方法,但它们并不漂亮或简单。
您可以强制用户重新上传&#34;一个带有<input type=file>
的下载文件来验证它,但这最好是麻烦的,本地文件浏览对话框可能会让一些人惊慌失措。它是确保下载的唯一确定方法,但对于非敏感应用程序来说,它非常严苛,并且它不会在某些移动设备上工作。缺乏文件支持的平台。
您也可以尝试从服务器端观看,向客户端发送文件在服务器上命中的消息。这里的问题是,一旦打开/保存对话框出现,下载就开始下载,尽管在后台无形中。这就是为什么你等了一会儿才能接受&#34;一个大文件,它似乎首先快速下载。从服务器的角度来看,无论用户做什么,活动都是相同的。
对于一个巨大的文件,您可能会检测到整个文件不已转移,这意味着用户点击了&#34;取消&#34;,但它是一个复杂的同步过程将状态从后端推送到客户端。它需要大量的自定义编程,包括套接字,PHP消息传递,EventSource等,但收效甚微。它也是一场与时间赛跑的比赛,而且时间不确定;并且建议用户不要放慢下载速度。
如果它是一个小文件,它会在用户甚至看到对话框之前进行物理下载,因此服务器将无用。还要考虑一些下载管理器扩展接管作业,并且不保证它们的行为与vanilla浏览器相同。强迫等待可能会让那些硬盘速度慢的人永远畏缩不前。到&#34;完成&#34;下载;我们都经历过这种情况,并且在&#34; spinny&#34;风度下降会降低用户满意度,温和地说。
简而言之,没有简单的方法,除了您知道需要很长时间下载的大文件外,一般都没办法。我花了很多血汗和泪水试图提供我的download.js用户的能力,但根本没有好的选择。 Ryan dahl最初编写了node.js,因此他可以为他的用户提供一个上传进度条,也许有人会制作一个服务器/客户端软件包,以方便下载。
答案 1 :(得分:1)
jquery.fileDownload允许您这样做:
$(document).on("click", "a.fileDownloadPromise", function () {
$.fileDownload($(this).prop('href'))
.done(function () { alert('File download a success!'); })
.fail(function () { alert('File download failed!'); });
return false;
});
看看Github:
答案 2 :(得分:1)
鉴于用户是或应该知道应该在下一个步骤之前下载文件,用户应该期望某种形式的确认文件已被下载。
您可以使用<a>
元素创建一个唯一的标识符或时间戳,以包含在已下载的文件名中,download
属性设置为修改后的文件名。
在click
元素<button>
上.click()
点<a>
个href
元素,Blob URL
元素a
将click
设置为.click()
文件。在<input type="file">
元素change
处理程序.click()
元素上调用window.addEventListener("load", function() {
let id, filename, url, file;
let confirmed = false;
const a = document.querySelector("a");
const button = document.querySelector("button");
const confirm = document.querySelector("input[type=file]");
const label = document.querySelector("label");
function confirmDownload(filename) {
if (confirmed) {
filename = filename.replace(/(-\d+)/, "");
label.innerHTML = "download of " + filename + " confirmed";
} else {
confirmed = false;
label.innerHTML = "download not confirmed";
}
URL.revokeObjectURL(url);
id = url = filename = void 0;
if (!file.isClosed) {
file.close()
}
}
function handleAnchor(event) {
confirm.click();
label.innerHTML = "";
confirm.value = "";
window.addEventListener("focus", handleCancelledDownloadConfirmation);
}
function handleFile(event) {
if (confirm.files.length && confirm.files[0].name === filename) {
confirmed = true;
} else {
confirmed = false;
}
confirmDownload(filename);
}
function handleDownload(event) {
// file
file = new File(["abc"], "file.txt", {
type: "text/plain",
lastModified: new Date().getTime()
});
id = new Date().getTime();
filename = file.name.match(/[^.]+/g);
filename = filename.slice(0, filename.length - 1).join("")
.concat("-", id, ".", filename[filename.length - 1]);
file = new File([file], filename, {
type: file.type,
lastModified: id
});
a.download = filename;
url = URL.createObjectURL(file);
a.href = url;
alert("confirm download after saving file");
a.click();
}
function handleCancelledDownloadConfirmation(event) {
if (confirmed === false && !confirm.files.length) {
confirmDownload(filename);
}
window.removeEventListener("focus", handleCancelledDownloadConfirmation);
}
a.addEventListener("click", handleAnchor);
confirm.addEventListener("change", handleFile);
button.addEventListener("click", handleDownload);
});
,其中附加的<button>download file</button>
<a hidden>download file</a>
<input type="file" hidden/>
<label></label>
事件用户应选择在用户操作下载的同一文件开始下载文件。
请注意以用户操作开头的comments
来电的链接。请参阅Trigger click on input=file on asynchronous ajax done()。
如果从用户文件系统中选择的文件等于修改的下载文件名,则调用函数,否则通知用户文件下载尚未确认。
8
9
10
10
9
8
答案 3 :(得分:1)
@dandavis
如果有人可以演示一种机械方式确定对任何文件的提示后用户操作,我将乐意为此赏金支付10倍!
你准备好了吗?因为这里是一个hacky解决方案?
我的StreamSaver库不使用blob下载a[download]
的文件。它使用Service Worker通过模拟服务器如何处理带有内容处置附件标头的下载内容,将某些内容流式传输到光盘上。
evt.respondWith(
new Response(
new ReadableStream({...})
)
)
现在您没有确切的方法来了解用户在对话框中按了什么,但是您有了有关流的一些信息。
如果用户在对话框中按“取消”或中止正在进行的下载,则该流也会中止。
保存按钮比较棘手。但是,让我们从highWaterMark的流存储桶可以告诉我们的内容开始。
在我的torrent example中登录writer.desiredSize
。它与愿意接收多少数据的关系。当您向流中写入内容时,它将减小所需的大小(无论是计数还是字节策略)。如果它从不增加,则意味着用户可能已暂停下载。当它下降到0以下时,您正在写入的数据超出了用户的要求。
您所做的每个大写写入都会返回一个诺言
writer.getWriter().write(uint8).then(() => {
// Chunk have been sent to the destination bucket
// and desiredSize increase again
})
当存储桶未满时,该承诺将解决。但这并不意味着该块已被写入磁盘,它仅意味着该块已从一个流传递到另一流(从写入->可读-> responseWith),并且通常在一开始就这样做。流,以及何时将另一个更早的块写入光盘。
即使用户没有选择孔数据是否可以存储在存储桶(内存)中,也有可能在用户做出选择之前完成写流
调整存储桶的大小,使其小于数据可以提供帮助
因此您可以假设何时 -下载开始 -完成 -并暂停 但是您不确定,因为您没有任何事件(除了关闭流的异常终止)
请注意,如果您不支持Transferable streams,那么torrent示例不会显示正确的大小,但是如果您在Service Worker内部进行所有操作,则可以解决此问题。 (而不是在主线程中完成)
检测流何时结束就像
readableStream.pipeTo(fileStream).then(done)
对于将来的参考,WICG/native-file-system可能会给您提供将文件写入光盘的权限,但是它必须解决提示对话框中的承诺,然后才能继续操作,并且可能正是用户所要求的
有一些将blob另存为流的示例,如果您感兴趣的话,甚至还有更多的multiple blob's as a zip
答案 4 :(得分:0)
我最近有一个涉及的项目要求我指定用户是否可以上传特定类型的文件,即(用户可以上传png而不是pdf)。
我可能没有使用最有效的方法,但最终我所做的是编写一个小的,内置的&#34; webapp&#34;用作文件浏览器,用于上传或下载。
我认为最接近的例子是没有发布我的秘密项目&#34;将是https://encodable.com/filechucker/
也许您可以编写一个简单的集成文件浏览器,例如云服务有时使用的文件浏览器(即dropbox),并且有一些功能可以检测带有自定义框和内容的输入。
只是一些想法。
答案 5 :(得分:-1)
试试这个:
<html>
<head>
<script type="text/javascript">
function Confirmation(pg) {
var res = confirm("Do you want to download?");
if(res){
window.open(pg,"_blank");
}
return res;
}
</script>
</head>
<body>
<a href="http://yoursite/download.zip" onclick="return Confirmation('http://yoursite/redirect.html');">download</a>
</body>
</html>