如何正确编码此URL

我试图使用JSoup获取此URL

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

即使使用编码,我也有例外。 我不明白为什么编码是错误的。 它回来了

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

而是正确的

http://sofzh.miximages.com/java/CIGUEÑAL.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()); } } 

编码没有错,这里的问题是复合unicode&precomposed unicode of character“Ñ”可以用2种方式显示,它们看起来相同但真的不同

 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 

这里发生了什么?

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

网络服务器是否正确?

对于那些好奇的人(像我一样),这篇关于多语言url的文章为这个主题带来了一些启示。 在有关IRI pathes (由Web服务器实际处理的部分)的部分中,它指出:

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

2.有关如何编码路径的更多信息,请参见第5.3.2.2节。 在IETF国际资源标识符(IRI)的标准rfc3987上 。 它说:

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

3. Unicode Consortium声明:

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

结论

问题中提到的网络服务器不符合IRI标准或unicode联盟的建议,并使用NFD编码代替NFC或NFKC。 正确编码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()调用encode() ,它使用NFC编码的unicode。 IDN.toASCII()将主机名转换为Punycode 。

非常简单的解决方案:编码系统提供和您需要的不同所以,以下解决方案将对您有所帮助。

 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编码之前将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依赖项:

   org.jsoup jsoup 1.11.2    com.google.guava guava 24.1-jre