Как правильно закодировать этот 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>
Другие вопросы по тегам