如何强制浏览器下载文件?

Posted

技术标签:

【中文标题】如何强制浏览器下载文件?【英文标题】:How to force browser to download file? 【发布时间】:2011-09-25 02:26:26 【问题描述】:

一切正常,但只有文件很小,大约 1MB,当我尝试使用更大的文件时,比如 20MB,我的浏览器会显示它,而不是强制下载,到目前为止我尝试了很多标题,现在我的代码看起来:

PrintWriter out = response.getWriter();
String fileName = request.getParameter("filename");

File f= new File(fileName);

InputStream in = new FileInputStream(f);
BufferedInputStream bin = new BufferedInputStream(in);
DataInputStream din = new DataInputStream(bin);

while(din.available() > 0)
    out.print(din.readLine());
    out.print("\n");


response.setContentType("application/force-download");
response.setContentLength((int)f.length());
response.setHeader("Content-Transfer-Encoding", "binary");
response.setHeader("Content-Disposition","attachment; filename=\"" + "xxx\"");//fileName);


in.close();
bin.close();
din.close();

【问题讨论】:

设置内容类型应该可以。是哪个浏览器? 哪个浏览器? Octet-stream 应该是正确的答案,但旧版本的 IE 过去常常尝试“嗅探”内容类型,而不管 Web 服务器告诉它什么 chrome、ff 3.6 和其他所有可能... 标题在前,数据在后,而不是其他方式! Content-Disposition 附件会做到这一点。请注意,应用程序/强制下载是某种特定的 hack,而不是标准,请阅读:media-division.com/… 【参考方案1】:

在将文件内容写入输出流后,您正在设置响应标头。在响应生命周期中设置标头已经很晚了。正确的操作顺序应该是先设置headers,再将文件内容写入servlet的outputstream。

因此,你的方法应该写成如下(这不会编译,因为它只是一个表示):

response.setContentType("application/force-download");
response.setContentLength((int)f.length());
        //response.setContentLength(-1);
response.setHeader("Content-Transfer-Encoding", "binary");
response.setHeader("Content-Disposition","attachment; filename=\"" + "xxx\"");//fileName);
...
...
File f= new File(fileName);

InputStream in = new FileInputStream(f);
BufferedInputStream bin = new BufferedInputStream(in);
DataInputStream din = new DataInputStream(bin);

while(din.available() > 0)
    out.print(din.readLine());
    out.print("\n");

失败的原因是 servlet 发送的实际标头可能与您打算发送的不同。毕竟,如果 servlet 容器不知道什么 headers(在 HTTP 响应中出现在 body 之前),那么它可能会设置适当的 headers 以确保响应有效;因此,在文件写入后设置标头是徒劳且多余的,因为容器可能已经设置了标头。您可以通过使用 Wireshark 或 Fiddler 或 WebScarab 等 HTTP 调试代理查看网络流量来确认这一点。

您还可以参阅ServletResponse.setContentType 的 Java EE API 文档以了解此行为:

设置发送给客户端的响应的内容类型,如果响应尚未提交。给定的内容类型可能包括字符编码规范,例如,text/html;字符集=UTF-8。如果在调用 getWriter 之前调用此方法,则仅从给定的内容类型设置响应的字符编码。

可以重复调用此方法来更改内容类型和字符编码。 如果在响应提交后调用此方法无效。

...

【讨论】:

application/force-download 是 hack,而不是标准:media-division.com/… @ChristopheRoussy,是的,没有人这样声称。停止在此处发送所有答案。 我想告诉其他 SO 用户它是一个 hack 以及它是如何工作的,因为这在任何地方都没有提到,这里是关于 SO 的解释:***.com/questions/10615797/…【参考方案2】:

在写出文件之前设置内容类型和其他标题。对于小文件,内容被缓冲,浏览器首先获取标题。对于大公司来说,数据是第一位的。

【讨论】:

【参考方案3】:

这是一个 php 脚本,它完美地解决了我测试过的每个浏览器的问题(FF 自 3.5、IE8+、Chrome)

header("Content-Disposition: attachment; filename=\"".$fname_local."\"");
header("Content-Type: application/force-download");
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".filesize($fname));

据我所知,您做的一切都是正确的。您检查过您的浏览器设置吗?

【讨论】:

我猜是“你做的一切都是正确的”让你大吃一惊。接受的答案清楚地表明了什么是不正确的。 application/force-download 是 hack,而不是标准

以上是关于如何强制浏览器下载文件?的主要内容,如果未能解决你的问题,请参考以下文章

如何强制浏览器下载 xml 文件?

如何在移动浏览器上使用 PHP 强制下载文件?

如何强制运行 swf 文件,而不是下载

十nginx 强制下载txt等文件

强制网络浏览器播放文件而不是下载

强制浏览器在点击时下载图像文件