通过 RFC 5987 处理带有空格的文件名* 参数会导致文件名中出现“+”

Posted

技术标签:

【中文标题】通过 RFC 5987 处理带有空格的文件名* 参数会导致文件名中出现“+”【英文标题】:handling filename* parameters with spaces via RFC 5987 results in '+' in filenames 【发布时间】:2012-07-03 09:07:29 【问题描述】:

我有一些我正在处理的遗留代码(所以不,我不能只使用带有编码文件名组件的 URL),它允许用户从我们的网站下载文件。由于我们的文件名通常使用多种不同的语言,它们都以 UTF-8 格式存储。我编写了一些代码来处理将 RFC5987 转换为正确的 filename* 参数。这很好用,直到我有一个包含非 ascii 字符 空格的文件名。根据 RFC,空格字符不是 attr_char 的一部分,因此它被编码为 %20。我有新版本的 Chrome 和 Firefox,它们都在下载时转换为 %20 到 +。我尝试不编码空间并将编码的文件名放在引号中并得到相同的结果。我已经嗅探了来自服务器的响应,以验证 servlet 容器没有与我的标头混淆,并且它们对我来说看起来是正确的。 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 = '!','#','$','&','\'','+','-','.','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();

更新

这是我在评论中提到的带有空格的中文文件的下载对话框的屏幕截图。

【问题讨论】:

这是导致此问题的示例标头:Content-Disposition:attachment;文件名*=UTF-8''Museum%20%5A%69%86.jpg 请参阅greenbytes.de/tech/tc2231/#attwithquotedsemicolon - 该测试用例在带引号的字符串中有一个空格字符,并且似乎可以在 Firefox 中使用。我们在测试不同的东西吗? 这看起来像别的东西。该测试检查带引号的字符串中的分号。我的问题是我有一个包含中文字符和空格的文件名,所以我使用的是 filename* 形式,并且使用了不带引号的标记形式,因为我阅读了一些建议不要使用带 % 转义的引号的文档。在我上面评论中的示例中,汉字被正确识别和转换,但 %20 被映射到 +。 Peter:我只是想了解测试用例在您的系统上的作用;你能检查一下吗?此外,您的代码看起来不正确,因为它实际上并没有首先转换为 UTF-8;您上面的示例在空格后仅包含三个(编码的)US-ASCII 字符。 朱利安:呃。我记得读过首先需要 UTF-8 转换并忘记添加它。这很可能是原因。现在重新编码以进行测试。 【参考方案1】:

正如 Julian 在 cmets 中指出的那样,我犯了一个菜鸟 Java 错误并且忘记了将字符转换为字节(因此我编码了字符的代码点而不是字符的字节表示),因此编码完全不正确。这在 RFC 5987 中明确提及为一项要求。我将发布更正的代码以进行转换。一旦编码正确,浏览器就可以正确识别filename*参数,并且用于下载的文件名是正确的。

以下是对字符串的 UTF-8 字节进行操作的更正转义码。给我带来麻烦的文件名现在正确编码如下所示:

内容配置:附件;文件名*=UTF-8''Museum%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();

【讨论】:

想指出字符 ' 不在 rfc5987 的允许列表中。 .. 但字符 `(重音符号,反引号)是。已编辑答案。

以上是关于通过 RFC 5987 处理带有空格的文件名* 参数会导致文件名中出现“+”的主要内容,如果未能解决你的问题,请参考以下文章

C 预处理器宏参数在末尾带有空格以进行连接?

如何将带有空格的路径作为参数添加到 CreateProcess 批处理文件?

windows qt如何通过命令行参数提取带有空格的文件名

如何使用带有路径的“开始”和带有空格的命令在 Windows 中创建批处理文件

在 Windows .cmd 文件或批处理文件的路径中设置带有空格的路径变量

shell脚本传参中包含有空格的参数