Любой способ заставить браузер по умолчанию Android распознавать имена файлов, отличных от ASCII, в загрузках "Content-Disposition: attachment"?

Прежде всего, я уверен, что это не дубликат, потому что я довольно долгое время изучал эту тему, как в StackOverflow, так и в других местах. Аналогичные вопросы были заданы, но никто не получил удовлетворительного ответа.

Связанные (но не идентичные) вопросы из прошлого:

Я также полностью осведомлен о трюках mod_rewrite, которые делают его совершенно ненужным для жонглирования имен файлов в HTTP-заголовках. Но предположим, что это не вариант.


Большинство современных браузеров (IE9 +, Firefox, Chrome) поддерживают RFC2231/5987 при загрузке файлов с не-ASCII-символами в своих именах. В таких случаях следующий 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 по умолчанию. (До сих пор я тестировал это в Gingerbread, Ice Cream Sandwich и Jelly Bean.)

Если вы даете ему стандартную обработку RFC2231/5987, браузер по умолчанию полностью игнорирует его и пытается угадать имя файла из последней части URL-адреса.

Если вы даете ему обычное нестандартное (IE <= 8) лечение, браузер по умолчанию пытается интерпретировать имя файла как ISO-8859-1, что приводит к неразборчивому беспорядку символов, или он молча отбрасывает все не-ASCII-символы. Точное поведение отличается от версий, но в любом случае понятно, что браузер по умолчанию для Android не был предназначен для поддержки формата rawurlencode().

То же самое происходит, если вы помещаете исходное имя файла в заголовок.

Обычно это не проблема с сторонними браузерами, такими как Firefox для Android, браузер Dolphin и браузер лодок. Браузерное приложение по умолчанию - единственное, что постоянно не понимает имена файлов UTF-8.


Возможно, это было окончательно исправлено в последней версии Android, или, возможно, оно будет исправлено в следующей версии. Но это не мой вопрос. Мне нужно это для работы на существующих устройствах, и есть еще миллионы устройств Gingerbread и ICS.

Я прочитал отчеты об ошибках, я прочитал жалобы, я прочитал почти все, что нужно прочитать об этой проблеме. До сих пор мне не удалось найти какую-либо схему кодирования, которая действительно работает.

Если кто-нибудь знает, как закодировать имя файла без имени ASCII ** (например, файла파일ファイル名.jpg) в заголовке Content-Disposition **, и узнайте его браузер по умолчанию Android, пожалуйста, поделитесь им! Меня не волнует, насколько он хакерский или нестандартный. Меня не волнует, нужно ли его настраивать для каждой версии Android.

Update

К сожалению, до сих пор я не получил ответа, который фактически решает проблему, упомянутую выше. Так щедрость истекает невостребованным. Пожалуйста, не отвечайте, если вы действительно не знаете, как кодировать неевропейские имена на смешанном языке таким образом, который распознается Android-браузером до ICS, или если у вас есть убедительные доказательства того, что это невозможно.

Ответ 1

URLUtil.java отвечает за guessFileName, который вызывает parseContentDisposition, который использует это регулярное выражение "attachment;\\s*filename\\s*=\\s*(\"?)([^\"]*)\\1\\s*$".

чтобы получить имя файла на основе заголовка Content-Disposition.

Исходный код, который пытается воспроизвести функциональность 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
        }

     }
}

Ответ 2

К сожалению, я не могу проверить это прямо сейчас, но я использую следующий код для отправки файла в браузеры, и до сих пор у меня не было никаких проблем (даже с браузерами для 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, которая преобразует любой символ не-utf8 в один ISO-8859-1