代码之家  ›  专栏  ›  技术社区  ›  Lucas

为什么Java自动解码URI编码的文件名中的%2f?

  •  6
  • Lucas  · 技术社区  · 16 年前

    我有一个servlet需要写出具有用户可配置名称的文件。我正试图使用URI编码来正确转义特殊字符,但JRE似乎自动转换编码的正斜杠。 %2F 进入路径分隔符。

    例子:

    File   dir = new File("C:\Documents and Setting\username\temp");
    String fn  = "Top 1/2.pdf";
    URI    uri = new URI( dir.toURI().toASCIIString() + URLEncoder.encoder( fn, "ASCII" ).toString() );
    File   out = new File( uri );
    
    System.out.println( dir.toURI().toASCIIString() );
    System.out.println( URLEncoder.encode( fn, "ASCII" ).toString() );
    System.out.println( uri.toASCIIString() );
    System.out.println( output.toURI().toASCIIString() );
    

    输出是:

    file:/C:/Documents%20and%20Settings/username/temp/
    Top+1%2F2.pdf   
    file:/C:/Documents%20and%20Settings/username/temp/Top+1%2F2.pdf
    file:/C:/Documents%20and%20Settings/username/temp/Top+1/2.pdf
    

    在实例化新的文件对象之后, %2F 序列被自动转换为正斜杠,最后得到的路径不正确。有人知道处理这个问题的正确方法吗?

    问题的核心似乎是

    uri.equals( new File(uri).toURI() ) == FALSE
    

    当有 %2F 在URI中。

    我计划只是逐字使用urlencoded字符串,而不是尝试使用 File(uri) 构造函数。

    2 回复  |  直到 10 年前
        1
  •  5
  •   BalusC    16 年前

    这个 new File(URI) 根据获取的路径构造文件 URI#getPath() 而不是你所期望的- URI#getRawPath() . 这看起来像是“按设计”的特色。

    您有两种选择:

    1. URLEncoder#encode() fn 两次(注: encode() 不是 encoder() )
    2. 使用 new File(String) 相反。
        2
  •  2
  •   Stephen C    16 年前

    我认为@balusc已经在您的代码中解决了直接的问题。我想指出另外一个问题

    这个 dir.toURI().toASCIIString() URLEncoder.encoder(fn, "UTF-8").toString() 表达式实际上做了很多不同的事情。

    • 第一个,将URI编码为字符串,根据URI语法应用URI编码规则。例如,路径组件中的“/”将不会被编码,但查询或片段组件中的“/”将被编码为%2f。

    • 第二个,编码 fn 应用编码规则而不引用字符串内容的字符串。

    这个 File(URI) 构造函数从文件URI到文件的映射是 system dependent and undocumented . 我有点惊讶它解码了 %2F ,但它做了它所做的,并且@balusc解释了原因。问题在于,使用一种明确依赖于系统的机制(“文件:”uri)可能存在问题。

    最后,这样组合这些URI组件字符串是错误的。它应该是

    URI uri = new URI(
            dir.toURI().toString() +
            URLEncoder.encoder(fn, "UTF-8").toString();
    

    URI uri = new URI(
            dir.toURI().toASCIIString() +
            URLEncoder.encoder(fn, "ASCII").toString());