任何使Android的默认浏览器识别“内容处置:附件”下载中的非ASCII文件名的方法


Any way to make Android's default browser recognize non-ASCII filenames in "Content-Disposition: attachment" downloads?

首先,我很确定这不是重复的,因为我已经在StackOverflow和其他地方研究这个主题很长一段时间了。也有人提出过类似的问题,但没有一个得到令人满意的答复。

过去的相关(但不完全相同)问题:

  • Android Chrome浏览器不必要地重命名下载文件的名称和类型
  • 如何在HTTP中对内容处置头的文件名参数进行编码?

我也完全了解mod_rewrite技巧,这些技巧使得完全没有必要在 HTTP 标头中处理文件名。但是,让我们假设这不是一种选择。


大多数现代浏览器(IE9+,Firefox,Chrome)在下载名称中包含非ASCII字符的文件时都支持RFC2231/5987。在这些情况下,以下 PHP 代码的工作方式类似于魅力:

header("Content-Disposition: attachment; " .
       "filename*=UTF-8''" . rawurlencode($filename));

IE <= 8 无法理解 RFC2231/5987,但以下代码大部分时间都有效。由于每个浏览器都试图在某种程度上模拟IE,因此这也适用于许多其他浏览器,例如Firefox。

header("Content-Disposition: attachment; " .
       'filename="' . rawurlencode($filename) . '"');

同时,Chrome <11和Safari <6似乎更喜欢以下内容,尽管它直接将非ASCII字符放在标题中。

header("Content-Disposition: attachment; filename=" . $filename);

目前为止,一切都好。


但是当涉及到Android的默认浏览器应用程序时,一切都崩溃了(到目前为止,我已经在姜饼,冰淇淋三明治和果冻豆中对此进行了测试。

如果给它标准的 RFC2231/5987 处理,默认浏览器会完全忽略它,并尝试从 URL 的最后一部分猜测文件名。

如果给它通常的非标准 (IE <= 8) 处理,则默认浏览器会尝试将文件名解释为 ISO-8859-1,从而导致难以理解的字符混乱,或者它会静默丢弃所有非 ASCII 字符。确切的行为因版本而异,但无论如何,很明显Android的默认浏览器也不是为支持rawurlencode()格式而设计的。

如果将原始文件名放在标题中,也会发生同样的事情。

对于第三方浏览器,例如 Firefox for Android、Dolphin Browser 和 Boat Browser,这通常不是问题。默认浏览器应用程序是唯一始终无法理解 UTF-8 文件名的应用程序。


也许这最终在最新版本的Android中得到了修复,或者可能会在下一个版本中修复。但这不是我的问题。我需要它在现有设备中工作,并且仍然有数百万个姜饼和ICS设备。

我已经阅读了

错误报告,我阅读了投诉,我已经阅读了几乎所有关于这个问题的内容。到目前为止,我无法找到任何实际有效的编码方案。

如果有人知道如何编码非 ASCII 文件名**(例如 файла파일ファイル名.jpg ) 在Content-Disposition **标题,并让 Android 默认浏览器识别它,请分享它!我不在乎它有多笨拙或非标准。我不在乎它是否需要为每个版本的Android进行定制。

更新

不幸的是,到目前为止,我还没有收到任何实际解决上述问题的答案。因此,赏金到期无人认领。请不要回答,除非您实际上知道如何以 ICS 之前 Android 浏览器识别的方式对非欧洲混合语言文件名进行编码,或者您有确凿证据表明这是不可能的。

URLUtil.java负责guessFileName调用使用此正则表达式parseContentDisposition "attachment;''s*filename''s*=''s*('"?)([^'"]*)''1''s*$"

以根据内容处置标头获取文件的文件名。

下面的源代码尝试复制parseContentDisposition功能,当我测试它时,它工作正常。例如,它返回файла파일ファイル名.jpg.

import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class HelloWorld{
     public static void main(String []args){
     String contentDisposition = "Content-Disposition: attachment; " + " filename=" +"'"файла파일ファイル名.jpg'"";     
     Pattern CONTENT_DISPOSITION_PATTERN = Pattern.compile("attachment;''s*filename''s*=''s*('"?)([^'"]*)''1''s*$",Pattern.CASE_INSENSITIVE);
        try {
            Matcher m = CONTENT_DISPOSITION_PATTERN.matcher(contentDisposition);
            if (m.find()) {
                System.out.println("Result: " + m.group(2));
            }
        } catch (IllegalStateException ex) {
             // This function is defined as returning null when it can't parse the header
        }
     }
}

可悲的是,我现在无法测试它,但我使用以下代码将文件发送到浏览器,到目前为止,我没有任何问题(即使使用 android 的浏览器)希望它可以帮助您:

    $formatRFC2231 = 'filename*=UTF-8''''%s';
    $formatDef = 'filename="%s"';
    switch ($useragent) {
        case "Opera":
        case "Firefox":
            $filename = rawurlencode($name);
            $format = $formatRFC2231;
            break;
        case "IE":
        case "Safari":
        case "Chrome":
            $filename = rawurlencode($name);
            $format = $formatDef;
            break;
        default:
            $filename = iconv("UTF-8", "ISO-8859-1//TRANSLIT", $name);
            $format = $formatDef;
            break;
    }

这里的关键点是 iconv 函数,它可以转换 ISO-8859-1 中的任何非 utf8 字符

我相信您在Android下载管理器中遇到了一个错误,如下所述:

https://code.google.com/p/chromium/issues/detail?id=162333