启动下载的最佳方式是什么?

12
在一个基于PHP的网站上,我想在用户填写短表单之后发送下载包。网站启动的下载应该类似于像download.com这样的网站,它会显示“您的下载即将开始”的提示。
我知道一些可能的方法和浏览器兼容性(基于快速测试):
1)使用指向新文件的window.open方法。
- FireFox 3 blocks this.  
 - IE6 blocks this.  
 - IE7 blocks this.

2) 创建一个指向新文件的iframe。

- FireFox 3 seems to think this is OK. (Maybe it's because I already accepted it once?)  
 - IE6 blocks this.  
 - IE7 blocks this.

How can I do this so that at least these three browsers will not object? 

额外奖励:是否有一种方法不需要浏览器条件语句?

(我相信download.com有条件地使用这两种方法,但我无法使它们任何一种起作用。)

回应和澄清:

Q: "Why not point the current window to the file?"  
A: That might work, but in this particular case, I want to show them some other content while their download starts - for example, "would you like to donate to this project?"

更新:我已经放弃了这种方法。请参见下面的答案以了解原因。

11个回答

17

您还可以进行元刷新,大多数浏览器都支持。Download.com会将其放置在noscript标记中。

<meta http-equiv="refresh" content="5;url=/download.php?doc=123.zip"/>

2
讨厌这些。有时需要等待几秒钟才能下载,而下载本身只需要1秒钟。我的浏览器保存浏览会话,这意味着当我重新启动浏览器时,文件会从无处冒出。我无法复制链接,必须在页面上寻找“如果……请点击此处”并停止自动下载。 - Kornel
同意,但这是他最初要求的。 - Soldarnal

15
更新:我已经决定放弃这种方法,而是向用户提供一个指向实际文件的链接。我的理由是:
当我尝试通过服务器启动下载时,浏览器阻止了我的尝试。这让我思考:“浏览器是正确的。它怎么知道这是个合法下载?如果不是明显由用户发起的下载,它应该阻止。”
任何我可以用于服务器启动下载的方法也可能被想要发送恶意软件的人使用。因此,仅当用户通过单击文件链接专门请求文件时才会进行下载。
你可以自由地持有不同意见,如果你仍然想启动下载,希望本主题能对你有所帮助。

5
我通常只有一个PHP脚本,直接将文件输出到浏览器,并使用适当的Content-Type。
if(file_exists($filename)) {
    header("Pragma:  public");
    header("Expires:  0");
    header("Cache-Control:  must-revalidate, pre-check=0");
    header("Cache-Control:  private", false);
    header("Content-Type:  " . $content-type);
    header("Content-Disposition:  attachment; filename=\"" . basename($filename) . "\";" );
    header("Content-Transfer-Encoding:  binary");
    header("Content-Length:  " . filesize($filename));

    readfile("$filename");
}else{
    print "ERROR:  the file " . basename($filename) . " could not be downloaded because it did not exist.";
}

唯一的缺点是,由于它设置了HTTP头,所以必须在有任何其他输出之前调用它。
但是,您可以在页面上添加指向PHP下载页面的链接,这将导致浏览器弹出一个下载框,而不会破坏当前页面的内容。

马克 - 这一定超出了我的能力范围; 我不理解关于头部的任何内容。这种方法是否允许您在下载开始时转到另一页? - Nathan Long
基本上你所做的是读取文件的二进制内容,并将该二进制数据发送到浏览器,而不是HTML。Content-Type只是告诉浏览器这是什么类型的文件。尝试创建一个简单的页面,只包含我的代码片段,并插入文件名和Content-Type。 - Mark Biek
一些有用的内容类型包括:application/zip pplication/pdf application/octet-stream application/access application/msword application/vnd.ms-excel application/vnd.ms-powerpoint image/gif image/png image/jpg - Mark Biek
你忽略了问题的一部分。'网站发起的下载应该类似于像download.com这样的网站,它们会显示"您的下载即将开始"。' - ceejayoz
1
请注意:您可以在此处获取一个不错的MIME类型数组http://snipplr.com/view.php?codeview&id=1937,格式为$ extension => $ mimeString。 - alex

1

嗨!

@Nathan: 我决定这样做:让我的“getfile.php”加载所有必要的东西,然后执行一个

header("Location: ./$path/$filename");

让浏览器自己直接处理文件,以其认为正确的方式进行操作。这在我的 Opera 浏览器中也可以正常工作。

但是,在不允许直接访问文件的环境中,这将成为一个问题,在这种情况下,您必须找到另一种方法!(感谢 Discordia,我的文件是公共 PDF!)

最好的问候,Basty


1

有一个问题是,如果标题没有被设置“正确”,则可能会在IE(尤其是版本6)中遇到问题。

确保设置正确的Content-Type,但也要考虑为IE(至少)设置缓存选项以允许缓存。如果文件是用户可以打开而不是保存的文件(例如MS Word文档),早期版本的IE需要缓存文件,因为它们将“打开”请求移交给适用的应用程序,并指向已在缓存中下载的文件。

如果IE6用户的缓存已满,则还存在相关问题,它将无法正确保存文件(因此,当适用的应用程序获得手动打开它的机会时,它将抱怨文件已损坏。

您可能还希望关闭下载时的任何gzip操作(对于IE)

IE6 / IE7在大型下载(例如4.x GB ...)方面都存在问题,这不太可能发生,因为IE甚至没有下载管理器,但是需要注意。

最后,如果从嵌套的iframe内部启动了下载“推送”,IE6有时不会很好地处理。我不确定是什么触发了这个问题,但我发现在IE6中避免这种情况更容易。


0
你可以使用JavaScript/jQuery来启动下载。以下是一个示例 - 您可以摆脱Ajax请求,只需使用setTimeout()块即可。
$("btnDownloadCSV").on('click', function() {
    $.ajax({
        url: "php_backend/get_download_url",
        type: 'post',
        contentType: "application/x-www-form-urlencoded",
        data: {somedata: "somedata"},
        success: function(data) {
            // If iFrame already exists, remove it.
            if($("[id^='iframeTempCSV_"]).length) { 
                $("[id^='iframeTempCSV_"]).remove();
            }
            setTimeout(function() {
                //  If I'm creating an iframe with the same id, it will permit download only the first time.
                // So randHashId appended to ID to trick the browser.
                var randHashId = Math.random().toString(36).substr(2);
                // Create a fresh iFrame for auto-downloading CSV
                $('<iframe id="iframeTempCSV_'+randHashId+'" style="display:none;" src="'+data.filepath+'"></iframe>').appendTo('body');
            }, 1000);
        },
        error: function(xhr, textStatus, errorThrown) {
           console.error("Error downloading...");
       }
    });
});

0
<a href="normaldownload.zip" onclick="use_dhtml_or_ajax_to_display_page()">

如果下载被保存,当前页面不受影响。只需确保下载不在同一窗口中打开(正确的 MIME 类型或 Content-Disposition),您就可以展示任何内容。

查看更完整的答案


0

改成指向新文件的位置怎么样?(例如通过更改 window.location)


0

我一直都是用指向文件的 iframe。

<iframe src="/download.exe" frameborder="0" height="0" width="0"><a href="/download.exe">Click here to download.</a></iframe>

0
关于不将当前窗口指向下载。 根据我的经验,您仍然可以显示“请捐赠”页面,因为下载(只要它们发送正确的标头)实际上不会更新浏览器窗口。 我在我的一个网站上为CSV导出执行此操作,对于用户而言,它只是弹出一个安全文件窗口。 因此,我建议像Soldarnal所示那样使用简单的元重定向。

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