【问题标题】:How to encode properly this URL如何正确编码此 URL
【发布时间】:2018-09-20 22:57:43
【问题描述】:

我正在尝试使用 JSoup 获取此 URL

http://betatruebaonline.com/img/parte/330/CIGUEÑAL.JPG

即使使用编码,我也遇到了异常。 我不明白为什么编码是错误的。它返回

http://betatruebaonline.com/img/parte/330/CIGUEN%C3%91AL.JPG

而不是正确的

http://betatruebaonline.com/img/parte/330/CIGUEN%CC%83AL.JPG

我该如何解决这个问题? 谢谢。

private static void GetUrl()
{
    try
    {
        String url = "http://betatruebaonline.com/img/parte/330/";
        String encoded = URLEncoder.encode("CIGUEÑAL.JPG","UTF-8");
        Response img = Jsoup
                            .connect(url + encoded)
                            .ignoreContentType(true)
                            .execute();

        System.out.println(url);
        System.out.println("PASSED");
    }
    catch(Exception e)
    {
        System.out.println("Error getting url");
        System.out.println(e.getMessage());
    }
}

【问题讨论】:

  • 嗯,这只是一个文件未找到异常,执行时是 404 作为 http 错误代码。请确保此时请求的url资源存在。
  • 首先,%C3%91 是一个完整的Ñ 字符,并且不需要事先N。所以N%C3%91 确实是 序列,而不是单个字符。

标签: java jsoup


【解决方案1】:

编码没有错,这里的问题是复合unicode和字符“Ñ”的precomposed unicode可以用两种方式显示,它们看起来一样但实际上不同

precomposed unicode: Ñ           -> %C3%91
composite unicode: N and ~       -> N%CC%83

我强调两者都是正确的,这取决于您想要哪种类型的 unicode:

String normalize = Normalizer.normalize("Ñ", Normalizer.Form.NFD);
System.out.println(URLEncoder.encode("Ñ", "UTF-8")); //%C3%91
System.out.println(URLEncoder.encode(normalize, "UTF-8")); //N%CC%83

【讨论】:

    【解决方案2】:

    这里发生了什么?

    正如@yelliver 所说,网络服务器似乎在其路径名中使用 NFD 编码的 unicode。所以解决方法也是使用相同的编码。

    网络服务器是否正确?

    1. 对于那些好奇的人(比如我),Multilingual Web Addresses 上的这篇文章为这个主题带来了一些启示。在section on IRI pathes(实际由网络服务器处理的部分)中,它声明:

    虽然域注册机构都可以同意接受特定形式和编码(基于 ASCII 的 punycode)的域名,但多脚本路径名标识位于多种平台上的资源,其文件系统将继续使用许多不同的编码。这使得路径比域名更难处理。

    2. 更多关于如何编码路径的主题可以在章节中找到 5.3.2.2。在 IETF 提出的国际化资源标识符 (IRI) 标准 rfc3987。它说:

    IRI 的等价性必须依赖于 IRI 是 适当的预字符规范化而不是应用字符 比较两个 IRI 时的归一化。例外是转换 从非数字形式,以及从非基于 UCS 的转换 字符编码为基于 UCS 的字符编码。在这些情况下, NFC 或使用 NFC 的标准化转码器必须用于 互操作性。为了避免假阴性和问题 转码,应该使用 NFC 创建 IRI。使用 NFKC 可能 避免更多问题;例如,通过选择半角拉丁语 字母而不是全角字母,全角而不是 半角片假名。

    3. Unicode Consortium 声明:

    NFKC 是标识符的首选形式,尤其是在存在安全问题的情况下(参见 UTR #36)。 NFD 和 NFKD 对内部处理最有用。

    结论

    问题中提到的网络服务器不符合 IRI 标准或 unicode 联盟的建议,并且使用 NFD 编码而不是 NFC 或 NFKC。 correctly encode an URL-String的一种方式如下

    URI uri = new URI(url.getProtocol(), url.getUserInfo(), IDN.toASCII(url.getHost()), url.getPort(), url.getPath(), url.getQuery(), url.getRef());
    

    然后将该 Uri 转换为 ASCII 字符串:

    String correctEncodedURL=uri.toASCIIString(); 
    

    toASCIIString() 调用使用 NFC 编码的 unicode 的 encode()IDN.toASCII() 将主机名转换为Punycode

    【讨论】:

    • 感谢您撰写解释。它可以帮助其他有相同或类似问题的人。
    • 有关此答案中提供的代码示例的进一步说明,请查看here
    【解决方案3】:

    其实在URL编码之前,你必须将URL转换成分解后的形式。

    这是一个使用 Guava 和 java.text.Normalizer 的解决方案:

    import com.google.common.escape.Escaper;
    import com.google.common.net.UrlEscapers;
    import org.jsoup.Connection;
    import org.jsoup.Jsoup;
    
    import java.text.Normalizer;
    
    public class JsoupImageDownload {
    
        public static void main(String[] args) {
    
            String url = "http://betatruebaonline.com/img/parte/330/CIGUEÑAL.JPG";
            String encodedurl = null;
            try {
                encodedurl = Normalizer.normalize(url, Normalizer.Form.NFD);
                Escaper escaper = UrlEscapers.urlFragmentEscaper();
                encodedurl = escaper.escape(encodedurl);
                Connection.Response img = Jsoup
                        .connect(encodedurl)
                        .ignoreContentType(true)
                        .execute();
    
                System.out.println(url);
                System.out.println("PASSED");
            } catch (Exception e) {
                System.out.println("Error getting url: " + encodedurl);
                System.out.println(e.getMessage());
            }
        }
    }
    

    这些是 Maven 依赖项:

    <!-- https://mvnrepository.com/artifact/org.jsoup/jsoup -->
    <dependency>
        <groupId>org.jsoup</groupId>
        <artifactId>jsoup</artifactId>
        <version>1.11.2</version>
    </dependency>
    
    <!-- https://mvnrepository.com/artifact/com.google.guava/guava -->
    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>24.1-jre</version>
    </dependency>
    

    【讨论】:

      【解决方案4】:

      非常简单的解决方案: 编码系统提供的和你需要的不同,所以下面的解决方案对你有好处。

      private static void GetUrl(String url)
      {
          try
          {
      
              String encodedurl = url.replace("Ñ","N%CC%83");
              Response img = Jsoup
                                  .connect(encodedurl)
                                  .ignoreContentType(true)
                                  .execute();
      
              System.out.println(url);
              System.out.println("PASSED");
          }
          catch(Exception e)
          {
              System.out.println("Error getting url");
              System.out.println(e.getMessage());
          }
      }
      

      【讨论】:

      • 问题在于,这可能是 url 列表中的另一个字符,并且代码无法在运行时失败。这就是为什么不能使用这种方法。
      • 该解决方案不正确。这将导致% 字符被编码为%25 序列,您将遇到更多麻烦。
      • 以下答案工作正常,但还有一个问题 Normalizer.Form.NFD 如何知道应该使用哪种形式,无论是 NFD 还是 NFC,以及另外 2 种可用类型。如果我们使用 NFD,这意味着我们认为 char 是复合 char,但每个 tie 都不是真的
      猜你喜欢
      • 1970-01-01
      • 2018-09-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-11-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多