在Android浏览器下载文件时避免内容类型问题

17
如果我想通过我的Web应用程序将文件提供给浏览器,我通常会将URL设置为类似于http://website.com/webapp/download/89347/image.jpg的东西。 然后我会设置HTTP标头Content-Type:application/octet-stream;filename=image.jpgContent-Disposition:Attachment
然而,在Android上。 似乎我唯一能让文件下载的方法是设置Content-Type:image/jpg。否则,文件名为<Unknown>,并出现错误。

下载失败
无法下载。 此手机不支持该内容。

有没有办法在Android上下载并通过浏览器打开文件而不保留MIME类型列表?

你好 George,我正在使用相同的技术来创建一个下载链接,但是我不知道在为锚标签准备下载链接时如何设置“HTTP头”。你能否详细解释一下? - nrsharma
设置HTTP头是在Web服务器上通过编程语言(如PHP、Java、CGI等)进行的。您是如何提供文件下载的? - 700 Software
请注意,这是一篇引用了早期安卓版本的较旧的帖子,可能包含过时的信息。Content-Disposition通常用于指示请求下载,而不仅仅是“查看”。 - 700 Software
5个回答

28
为了使任何下载在所有(特别是旧的)Android 版本上都能按预期工作,您需要执行以下操作:
  1. 将ContentType设置为application/octet-stream
  2. 将Content-Disposition文件名值放在双引号中
  3. 将Content-Disposition文件名扩展名写成大写字母
阅读我的博客文章以获取更多详细信息:
http://digiblog.de/2011/04/19/android-and-the-download-file-headers/

如果您遇到设备下载HTML页面而不是正确文件的问题,请参见StevePayne的答案。我通过将POST更改为GET来解决了这个问题。 - nidal
嗨,如果我从下载管理器下载图像,那么它再次打不开,显示“无法打开文件”错误。我已将请求的MIME类型设置为jpg。我还需要做什么? - Akash kumar
Android 4.0冰淇淋三明治。示例:“Content-Disposition:attachment; filename =“MyFileName.ZIP”;”将无法正常工作,因为在末尾有分号。所有Android版本的任何测试(和_摘要_)都有用吗?有用的网址:http://stackoverflow.com/questions/6319389/streaming-mime-type-application-pdf-from-asp-app-fails-in-google-chrome“Google Chrome v12发布引入了一个错误,触发了您描述的问题。 您可以通过发送Content-Length标头来解决它https://bugs.chromium.org/p/chromium/issues/detail?id=85549 - Kiquenet
2
#3 绝对是必须的。我已经设置了服务器以发送自己的MIME类型,但Android仍将其保存为application/octet-stream。当我更改文件扩展名为大写时,它起作用了。 - kuchi
感谢 @kuchi 的反馈。我简直不敢相信这些东西仍然有用。当我在2011年写这篇文章时,我们使用的是Android 2.1 !!! - Jpsy

10
如果您下载的文件中出现了 HTML 页面,我怀疑这是由于双重 HttpRequest GET 问题导致的。一个典型的场景是以下的 POST、重定向、GET 模型:
  • Android 浏览器向服务器发出 HttpRequest POST(例如,提交按钮或链接请求下载文件,假设为 filename.ext)

  • 服务器将所请求的 filename.ext 以字节流的形式存储在会话变量中,然后发出 Response.Redirect 到 Download.aspx,以处理响应对象的构造。

  • Android 浏览器正确地向服务器发送 HttpRequest GET 请求,以获取 Download.aspx。

  • 服务器使用包含所请求的 filename.ext 的响应对象以典型的 Content-Disposition: attachment; filename="filename.ext" 样式构造响应,其中 filename.ext 是会话变量中的字节。

  • Android 下载管理器随后发送另一个 HttpRequest GET 请求以获取 Download.aspx。我认为下载管理器将前面的 "attachment" 响应解释为触发发送第二个 GET 请求。

  • 服务器(Download.aspx)再次尝试构造响应对象并将其发送回浏览器。

  • Android 下载管理器使用第二个 Download.aspx 的响应对象内容来下载 filename.ext。

在许多情况下,这是可以接受的。但是,如果例如 Download.aspx 代码中的服务器进行一些清理并在第一次调用时删除会话变量,则下一次将没有会话变量。因此,根据代码编写方式,可能会出现响应对象未被显式构造以及 Response.End 未被调用,因此只有 Download.aspx 的 HTML 被发送。
这就是我们使用 Wireshark 发现的情况,尽管我承认我假设 Android 下载管理器是双重 GET 请求的原因。
希望这种解释对您有所帮助。

3
Steve,你的解释非常符合我博客文章的评论者的观察结果(在我上面的回答中提到)。即使在较高版本的Android(至少到3.2.1)中,这似乎也是一个问题,并且似乎取决于所使用的浏览器(Dolphin和Android原生受影响,FF和Opera不受影响)。一个可能的解决方案是用GET替换第一个请求或将所有所需数据编码(如可行)到下载请求的URL中。 谢谢,Jörg。 - Jpsy
1
我在Android论坛中发现了问题17803948,它们描述了股票浏览器和Android下载管理器无法正确处理通过POST请求启动的下载的能力。这两份报告已经得到了论坛管理员的确认,但被标记为“增强”而不是错误。1780已经超过3年了,被100多个访问者收藏,但仍然没有迹象表明它已经解决,甚至在最新的Android版本(目前为4.0.4)中也没有。 - Jpsy
有人找到解决方案了吗?这个问题仍然有效。在下载期间,我在服务器上收到了两个请求。是否可能找出哪个请求是伪造的? - Diyko

2

如我在下载Android文件中所写:

Android浏览器无法下载按钮Post事件中的文件。在Post事件中,文件将是一些.htm垃圾文件。要解决此问题,请按照以下步骤进行操作。

在下载按钮上单击

 protected void ImageButton1_Click(object sender, ImageClickEventArgs e)
    {
        Response.Redirect("download-file.aspx");
    }

and on  download-file.aspx file do as below

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

public partial class mobile_download_file : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        string filename = "usermanual.pdf";
        Response.ContentType = "application/octet-stream";
        Response.AppendHeader("Content-Disposition", "attachment; filename=" + "" + filename + "");
        Response.Write(Server.MapPath(Request.ApplicationPath) + "\\" + filename);
        Response.TransmitFile(Server.MapPath(Request.ApplicationPath) + "\\" + filename);
        Response.End();
    }
}

the same can be implemented in php also.

你好Martin,欢迎来到StackOverflow。1. 我认为POST请求不是问题的一部分,但这并不重要,也许你的答案对某些人有帮助。2. 我认为代码示例在这里不相关。我不使用php或aspx。3. 我建议用文本替换代码示例,例如“您需要创建一个重定向作为对POST的响应,然后从您重定向到的URL下载文件”。那些寻找代码示例的人应该发布他们语言的问题。 - 700 Software
你测试了哪些 Android 版本? - Kiquenet

0

我已经尝试了Jspy博客中的所有建议,但迄今为止都没有起作用。Content-disposition会使浏览器进入下载模式,但除了从下载发起页面的HTML之外,没有任何东西被下载。因此我的结论是,这是Google的纯粹错误,我们只能祈求Google修复它。我的解决方法是将内容类型设置为来自移动浏览器的Accept头的某种类型。通常可以正常工作,甚至可以将zip文件下载为文本。


4
Dmitriy,如果你的Android下载了源页面的HTML(!),那么你的服务器脚本逻辑肯定有问题。如果下载失败是在Android端,则不可能在文件中获取源HTML - 通常会获取下载内容但不会写入文件或一般性错误信息。但是此时源HTML不可用。想想看。 - Jpsy

0
理论上,文件名参数应该设置在Content-Disposition而不是Content-Type上。不确定这是否对Android浏览器有帮助。

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