Как правильно закодировать этот URL
Я пытаюсь получить этот URL с помощью JSoup
http://betatruebaonline.com/img/parte/330/CIGUE%C3%91AL.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());
}
}
4 ответа
Кодировка не неправильная, проблема здесь в том, что составной Unicode & предварительно составленный Unicode символа "С" может отображаться двумя способами, они выглядят одинаково, но на самом деле разные
precomposed unicode: Ñ -> %C3%91
composite unicode: N and ~ -> N%CC%83
Я подчеркиваю, что ОБА ПРАВИЛЬНЫ, это зависит от того, какой тип юникода вы хотите:
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 юникод в своих путевых именах. Поэтому решение состоит в том, чтобы использовать ту же кодировку.
Правильно ли работает веб-сервер?
1. Для тех, кому любопытно (как и я), эта статья о многоязычных веб-адресах проливает свет на эту тему. В разделе о IRI-патчах (часть, которая фактически обрабатывается веб-сервером), говорится:
Принимая во внимание, что все органы регистрации доменов могут согласиться принимать доменные имена в определенной форме и кодировке (punycode на основе ASCII), имена путей из нескольких сценариев идентифицируют ресурсы, расположенные на многих типах платформ, чьи файловые системы используют и будут продолжать использовать множество различных кодировок. Это делает путь намного более сложным для обработки, чем имя домена.
2. Подробнее о том, как кодировать пути, можно узнать в разделе 5.3.2.2. в предлагаемом стандарте IETF по интернационализированным идентификаторам ресурсов (IRI) rfc3987. Это говорит:
Эквивалентность ИРИ ДОЛЖНА опираться на допущение, что ИРИ соответствующим образом предварительно нормализованы, а не применяют нормализацию символов при сравнении двух ИРИ. Исключениями являются преобразование из нецифровой формы и преобразование из кодировки символов не на основе UCS в кодировку символов на основе UCS. В этих случаях для совместимости ДОЛЖНЫ использоваться NFC или нормализующий транскодер, использующий NFC. Чтобы избежать ложных негативов и проблем с транскодированием, ИРИ ДОЛЖНЫ быть созданы с использованием NFC. Использование NFKC может избежать еще больших проблем; например, выбрав латинские буквы половинной ширины вместо полных, и катаканы полной ширины вместо полуширины.
3. Консорциум Unicode заявляет:
NFKC является предпочтительной формой для идентификаторов, особенно там, где есть проблемы с безопасностью (см. UTR #36). NFD и NFKD наиболее полезны для внутренней обработки.
Заключение
Упомянутый в этом вопросе веб-сервер не соответствует рекомендациям стандарта IRI или консорциума Unicode и использует кодирование NFD вместо NFC или NFKC. Один из способов правильно кодировать строку URL-адреса заключается в следующем
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:
<!-- 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>