обработка параметров имени файла * с пробелами через RFC 5987 приводит к появлению '+' в именах файлов

У меня есть какой-то устаревший код, с которым я имею дело (поэтому я не могу просто использовать URL с закодированным компонентом имени файла), который позволяет пользователю загружать файл с нашего веб-сайта. Поскольку наши имена файлов часто бывают на разных языках, все они хранятся в формате UTF-8. Я написал некоторый код для обработки преобразования RFC5987 в правильный параметр имени файла *. Это прекрасно работает, пока у меня есть имя файла с не-ASCII символовand пространства. В соответствии с RFC символ пробела не является частью attr_char, поэтому он кодируется как% 20. У меня есть новые версии Chrome, а также Firefox, и все они конвертируются в% 20 + после загрузки. Я попытался не кодировать пространство и положить закодированное имя файла в кавычки и получить тот же результат. Я прослушал ответ от сервера, чтобы убедиться, что контейнер сервлета не перебивал мои заголовки, и они выглядят корректно для меня. В RFC даже есть примеры, содержащие% 20. Я что-то упустил или у всех этих браузеров есть ошибка, связанная с этим?

Спасибо заранее. Код, который я использую для кодирования имени файла, приведен ниже.

Питер

public static boolean bcsrch(final char[] chars, final char c) {
    final int len = chars.length;
    int base = 0;
    int last = len - 1; /* Last element in table */
    int p;

    while (last >= base) {
        p = base + ((last - base) >> 1);

        if (c == chars[p])
            return true; /* Key found */
        else if (c < chars[p])
            last = p - 1;
        else
            base = p + 1;
    }

    return false; /* Key not found */
}

public static String rfc5987_encode(final String s) {
    final int len = s.length();
    final StringBuilder sb = new StringBuilder(len << 1);
    final char[] digits = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
    final char[] attr_char = {'!','#','

Update

Вот снимок экрана диалога загрузки, который я получаю для файла с китайскими иероглифами с пробелами, как упомянуто в моем комментарии.

screen cap of download dialog

,'&','\'','+','-','.','0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','^','_','a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','|', '~'}; for (int i = 0; i < len; ++i) { final char c = s.charAt(i); if (bcsrch(attr_char, c)) sb.append(c); else { final char[] encoded = {'%', 0, 0}; encoded[1] = digits[0x0f & (c >>> 4)]; encoded[2] = digits[c & 0x0f]; sb.append(encoded); } } return sb.toString(); }

Update

Вот снимок экрана диалога загрузки, который я получаю для файла с китайскими иероглифами с пробелами, как упомянуто в моем комментарии.

screen cap of download dialog

 Julian Reschke03 июл. 2012 г., 08:21
Увидетьgreenbytes.de/tech/tc2231/#attwithquotedsemicolon - этот тестовый пример имеет пробел в строке в кавычках и, похоже, работает в Firefox. Мы тестируем разные вещи?
 Peter Friend03 июл. 2012 г., 09:48
Это похоже на что-то еще. Этот тест проверяет точку с запятой в строке в кавычках. Моя проблема в том, что у меня есть имя файла с китайскими символами, а также пробелы, поэтому я использую форму имени файла * и форму токена без кавычек, так как я прочитал некоторые документы, которые рекомендовали не использовать кавычки с% escapes. В примере из моего комментария выше китайские символы распознаются и конвертируются правильно, но% 20 отображается на +.
 Peter Friend03 июл. 2012 г., 02:00
Вот пример заголовка, который вызывает эту проблему: Content-Disposition: attachment; Имя файла * = UTF-8 & APOS; & APOS; Музей% 20% 5A% 69% 86.jpg
 Peter Friend03 июл. 2012 г., 10:36
Джулиан: Тьфу. Я помню, как читал, что сначала нужно было преобразовать UTF-8, и забыл добавить его. Это, скорее всего, причина. Перекодирую сейчас для тестирования.
 Julian Reschke03 июл. 2012 г., 10:19
Питер: я просто пытался понять, что тестовый пример делает в вашей системе; вы не могли бы проверить? Кроме того, ваш код выглядит неверно в том смысле, что он на самом деле не конвертируется в UTF-8 первым; В приведенном выше примере просто есть три (закодированных) символа US-ASCII после пробела.

Ответы на вопрос(1)

Решение Вопроса

Таким образом, как указал Джулиан в комментариях, я сделал ошибку Java-новичка и забыл выполнить преобразование моего символа в байт (таким образом, я закодировал кодовую точку символа вместо байтового представления символа), поэтому кодирование было совершенно неправильным. Это четко указано как требование в RFC 5987. Я буду публиковать исправленный код для выполнения преобразования. Как только кодировка верна, параметр filename * правильно распознается браузером, а имя файла, использованное для загрузки, является правильным.

Ниже приведен исправленный escape-код, который работает с байтами UTF-8 строки. Имя файла, которое доставляло мне неприятности, теперь правильно закодировано, выглядит так:

Content-Disposition: вложение; Имя файла * = UTF-8 & APOS; & APOS; музея% 20% E5% 8D% 9A% E7% 89% A9% E9% A6% 86.jpg

public static String rfc5987_encode(final String s) throws UnsupportedEncodingException {
    final byte[] s_bytes = s.getBytes("UTF-8");
    final int len = s_bytes.length;
    final StringBuilder sb = new StringBuilder(len << 1);
    final char[] digits = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
    final byte[] attr_char = {'!','#',','&','+','-','.','0','1','2','3','4','5','6','7','8','9',           'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z','^','_','`',                        'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z','|', '~'};
    for (int i = 0; i < len; ++i) {
        final byte b = s_bytes[i];
        if (Arrays.binarySearch(attr_char, b) >= 0)
            sb.append((char) b);
        else {
            sb.append('%');
            sb.append(digits[0x0f & (b >>> 4)]);
            sb.append(digits[b & 0x0f]);
        }
    }

    return sb.toString();
}
 11 сент. 2013 г., 09:02
.. но характер` (серьезный символ акцента, backtick) есть. Отредактировал ответ.
 11 сент. 2013 г., 08:46
Хотел бы отметить, что персонаж' отсутствует в списке разрешенных rfc5987.

Ваш ответ на вопрос