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

brc7rcf0  于 2023-03-16  发布在  Java
关注(0)|答案(3)|浏览(164)

我正在处理一些遗留代码(所以不,我不能只使用带有编码文件名组件的URL),它允许用户从我们的网站下载文件。由于我们的文件名通常是许多不同的语言,它们都存储为UTF-8。我写了一些代码来处理RFC 5987转换为适当的filename* 参数。这在我的文件名带有非ascii字符 * 之前工作得很好* 和**空格。根据RFC,空格字符不是attr_char的一部分,因此它被编码为%20。我有新版本的Chrome和Firefox,它们在下载时都转换为%20到+。我尝试过不对空格进行编码,并将编码的文件名放在引号中,得到了相同的结果。我嗅探了来自服务器的响应,以验证servlet容器不是“不要弄乱我的头文件,它们看起来是正确的。RFC甚至有包含%20的例子。我错过了什么,还是所有这些浏览器都有与此相关的bug?
非常感谢提前。代码我用来编码的文件名如下。
彼得

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();
}

更新

这是一个下载对话框的屏幕截图,我得到了一个文件与中文字符与空格,如我在评论中提到的。

kognpnkq

kognpnkq1#

所以正如Julian在评论中指出的,我犯了一个新手Java错误,忘记进行字符到字节的转换(因此我编码了字符的代码点而不是字符的字节表示),因此编码是完全错误的。这在RFC 5987中作为一个要求明确提到。我将发布进行转换的更正代码。一旦编码正确,浏览器正确识别filename* 参数,并且用于下载的文件名是正确的。
下面是正确的转义码,它对字符串的UTF-8字节进行操作。给我带来麻烦的文件名,现在正确编码如下:
内容处置:附件;文件名 *=UTF-8 ''博物馆%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();
}
agyaoht7

agyaoht72#

2022年更新

这个答案通过提供有关apache库的信息添加到answer from 10 years ago中,该库具有根据RFC 5987编码和解码字符串的方法。
RFC5987编码器和解码器在org.apache.cxf.attachment.Rfc5987Util类中可用。
我可以通过添加依赖项将jar导入到我的maven项目中:

<dependency>
   <groupId>org.apache.cxf</groupId>
   <artifactId>cxf-core</artifactId>
   <version>3.5.2</version>
</dependency>

(在https://jar-download.com/artifacts/org.apache.cxf/cxf-core上检查最新版本)

测试示例

@Test
public void verifyRfc5987EncodingandDecoding() throws UnsupportedEncodingException {
   final String s = "!\"$£%^&*()_-+={[}]:@~;'#,./<>?\\|✓éèæðŃœ";

   assertThat(Rfc5987Util.decode(
         Rfc5987Util.encode(s, "UTF-8"),
               "UTF-8"),
         equalTo(s));
}
oknrviil

oknrviil3#

除了@matt-wallis的回答:如果您已经在项目中使用org.springframework:spring-web,您可能需要使用ContentDisposition-builder:

String contentDispositionHeaderValue = ContentDisposition.attachment()
    .filename(someFilename, StandardCharsets.UTF_8)
    .build()
    .toString();
response.addHeader("Content-Disposition", contentDispositionHeaderValue);

参见https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/ContentDisposition.html

相关问题