首先,我很确定这不是重复的,因为我已经在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