开始多个下载并在下载提示窗口弹出时获得反馈

19

我正在编写一个小程序,它生成一个HTML文件并使用默认浏览器打开该文件以开始多个下载。

我不想为每个下载打开新的选项卡/窗口,因此为下载创建隐藏的iframe似乎是一个好的解决方案。

我在iframe上使用onload来查找每个下载提示是否已经显示。 然而,在Internet Explorer中,这种方法似乎非常不可靠。

因此,我想知道是否有方法可以修复这个问题或者是否有更好的方法?(请不要使用库)

以下是我的 HTML/JS 代码:

<!DOCTYPE html>
<!-- saved from url=(0016)http://localhost -->
<html><head>
  <meta content="text/html;charset=utf-8" http-equiv="Content-Type">
  <meta content="utf-8" http-equiv="encoding">
  <title>Downloads</title>
  <script>
    "use strict";
    var downloadsInfo = {
        "http://download-installer.cdn.mozilla.net/pub/firefox/releases/26.0/win32/en-US/Firefox%20Setup%2026.0.exe":"Status: Connecting",
        "http://download.piriform.com/ccsetup410.exe":"Status: Connecting"
    };
    var i = 0;
    var iv = setInterval(function() {
        i = ++i % 4;
        var j = 0;
        var finished = true;
        for (var key in downloadsInfo) {
            var value = downloadsInfo[key];
            if (value != "Status: Download Started!") {
                value = value+Array(i+1).join(".");
                finished = false;
            }
            document.getElementsByTagName("div")[j].innerHTML = key+"<br/>"+value;
            j = j+1;
        }
        if (finished) {
            alert('Done! You can close this window/tab now.');
            clearInterval(iv);
        }
    }, 800);
  </script>
</head><body>
  <h3>Please wait for your downloads to start and do not reload this site.</h3>
  <div></div> <br/><br/>
  <div></div> <br/><br/>
  <iframe src="http://download-installer.cdn.mozilla.net/pub/firefox/releases/26.0/win32/en-US/Firefox%20Setup%2026.0.exe" onload="downloadsInfo['http://download-installer.cdn.mozilla.net/pub/firefox/releases/26.0/win32/en-US/Firefox%20Setup%2026.0.exe'] = 'Status: Download Started!';" style="display:none"></iframe>
  <iframe src="http://download.piriform.com/ccsetup410.exe" onload="downloadsInfo['http://download.piriform.com/ccsetup410.exe'] = 'Status: Download Started!';" style="display:none"></iframe>
</body></html>

你对不可靠的IE有何目的? - vahid kargar
为什么在Internet Explorer中似乎非常不可靠?您在IE中有任何错误吗?如果有,您可以展示它们吗? - Cliff Burton
在我的IE 11中,它并不总是有效。下载总是开始了,但有时不会调用onload。 - Forivin
使用下载脚本,这样您就不必依赖iframe hack。 - dandavis
@Forivin:你有想到任何解决方案吗?我对此很感兴趣 :) - Lain
显示剩余5条评论
3个回答

4
很简单,你无法知道本地浏览器下载是否已开始。每个浏览器处理方式都不同,用户可能设置浏览器提示位置,也可能直接自动下载到“下载”文件夹(现在大多数浏览器的默认值)。如果他正在询问位置,他可能会错误地取消,但是你的设置仍然会声称下载已经开始。因此,如果使用本地浏览器下载机制,则没有任何可靠的方法通知用户他们可以关闭标签一旦所有下载都已开始/完成......

要实现此效果的方法可能是先使用Javascript下载文件(需要您访问这些文件,当然不能选择第三方文件的热链接)。要在mega.nz上尝试下载文件,请查看此操作方式。我计划手写如何执行此操作,但是已有一个不错的(相当过时)答案列出了这个过程

如果意图只是确保下载已经开始,您可以在后端实现触发器来记录文件何时被访问。最简单的形式如下所示:
  1. 页面 download.html 请求 file.php?location=[...]&randomHash=1234
  2. 一旦 file.php 实际加载,它将在内存或数据库中设置一个标志,说明 randomHash id 1234 已经开始。
  3. file.php302 标头将页面重定向到实际的文件位置。
  4. download.html 使用 Ajax 定期检查是否已经提高了标志 randomHash=1234。如果是,则知道下载已经开始。

我不需要知道下载是否实际开始,我只想知道与下载文件相关的事情是否发生,比如“另存为” / 下载提示,自动下载或其他任何情况。 - Forivin
这样行不通。没有服务器。就像我说的,HTML文件将在本地生成并打开。 - Forivin
@Forivin 如果你在客户端上有一个可执行文件,我建议你直接使用它来进行下载,而不是试图将其交给浏览器。 - David Mulder
@Forivin,uGet是一个开源(LGPL)下载管理器,也许你可以考虑捆绑它?在这种情况下,这似乎是一个相当简单的解决方案,只要你链接到下载管理器源代码,我认为你应该在许可证方面没问题(不过还是要检查一下)。 - David Mulder
不,我不在寻找替代方案。下载需要在浏览器中进行。 - Forivin
显示剩余2条评论

1

事实上,据报道IE在处理iframe的onload事件处理程序时并不总是表现良好。有一个已经打开的活动bug跟踪记录


问题在网上的许多地方都有讨论,似乎最可靠的解决方案是使用嵌套iframe进行间接下载:iframe加载一个包含要下载文件的iframe的HTML文件。原因是IE似乎不喜欢指向除HTML以外的其他内容的iframe。所以如果您的程序有可能这样做,请尝试使用这种方法。
  1. For each file to download, generate a HTML file with a body that looks like this:

    <iframe src="http://filetodownload.exe" style="display:none"></iframe>
    
  2. Store this file in a temporary folder, e.g. C:\tmp\filetodownload.html

  3. In your "master" generated HTML file, replace the iframe source with this intermediate file:

    <iframe src="C:\tmp\filetodownload.html" 
            onload="downloadsInfo['http://filetodownload.exe']='Status: Download Started!';" 
            style="display:none"></iframe>
    

这可能会奏效。但按照IE的传统,这取决于具体情况是否能够奏效...


如果无法正常工作,一些已被证明有用的解决方案包括:
  • Put the onload handler in a function, and write in the definition of the iframe: onload="return theonloadfunction()" (even if the function does not return anything)
  • Instead of using the onload attribute, attach the event handler in javascript, like so:

    iframe = document.getElementById("theiframeid")
    iframe.attachEvent("onload", theonloadfunction);`
    

请注意,attachEvent只适用于IE浏览器。如果您想支持其他浏览器,您需要检测并在非IE情况下使用addEventListener

最后,您可以尝试这些解决方案的两个或多个的组合:)


-3
<html>
    <head>
        <meta content = 'text/html;charset=utf-8' http-equiv = 'Content-Type'>
        <meta content = 'utf-8' http-equiv = 'encoding'>

        <script>
            /*
                First, I removed the setInterval(). Since you rely on the onload property we can aswell just check it on each onload.
                Second, I changed your downloadsInfo to an object array.

                Also be aware while testing, that some browsers cache your cancel/block choice and do not reask again for the same url.
                Additionally firefox does not fire on frame downloads.
                Furthermore the alert in your test might not show for overlapping or setting reasons.
            */

            var downloadsInfo = [
                {url: "http://download-installer.cdn.mozilla.net/pub/firefox/releases/26.0/win32/en-US/Firefox%20Setup%2026.0.exe", Status: "Connecting"},
                {url: "http://download.piriform.com/ccsetup410.exe", Status: "Connecting"}
            ];

            //IE has a problem in sometimes merely firing the onload propery once, which we bypass by dynamically creating them
            //It is also less limited.
            function iframeConnect(){
                for(var i=0, j=downloadsInfo.length; i<j; i++){
                    var tF = document.createElement('iframe');
                    tF.arrayIndex = i; //For convenience
                    tF.style.display = 'none';

                    //Normal load event, working in ie8-11, chrome, safari
                    tF.onload = function(){
                        iframeExecuted(this.arrayIndex);
                    };

                    //Workaround for firefox, opera and some ie9
                    tF.addEventListener('DOMSubtreeModified', function(){
                        iframeExecuted(this.arrayIndex);
                    }, false);

                    document.body.appendChild(tF);
                    tF.src = downloadsInfo[i].url;
                }
            }

            function iframeExecuted(i){
                downloadsInfo[i].Status = 'Executed';

                var tStatus = iframeFinished();
                var tE = document.querySelector('h3');

                if (tStatus.Done) tE.innerHTML = 'Finished'
                else tE.innerHTML = 'Processed ' + tStatus.Processed + ' of ' + tStatus.Started;
            }

            function iframeFinished(){
                for(var i=0, j=downloadsInfo.length; i<j; i++){
                    if (downloadsInfo[i].Status != 'Executed') break;
                }
                //Note that the Processed value is not accurate, yet it solves is testing purpose.
                return {Done: (i == j), Processed: i, Started: j}
            }
        </script>
    </head>

    <body onload = 'iframeConnect()'>
        <h3>Please wait for your downloads to start and do not reload this site.</h3>
    </body>
</html>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接