Кодування URL-адрес HTTP в Java


366

Моя автономна програма Java отримує від користувача URL-адресу (яка вказує на файл), і мені потрібно натиснути її та завантажити її. Проблема, з якою я стикаюся, полягає в тому, що я не в змозі правильно кодувати URL-адресу HTTP ...

Приклад:

URL:  http://search.barnesandnoble.com/booksearch/first book.pdf

java.net.URLEncoder.encode(url.toString(), "ISO-8859-1");

повертає мене:

http%3A%2F%2Fsearch.barnesandnoble.com%2Fbooksearch%2Ffirst+book.pdf

Але те, що я хочу, це

http://search.barnesandnoble.com/booksearch/first%20book.pdf

(простір замінено на% 20)

Я думаю URLEncoder, не розроблено для кодування URL-адрес HTTP ... JavaDoc каже "Клас утиліти для кодування HTML-форм" ... Чи є інший спосіб це зробити?


1
Дивіться також stackoverflow.com/questions/10786042 / ...
Raedwald

Поведінка цілком правильна. Кодування URL-адреси - це перетворити щось у рядок, який можна сміливо передавати як параметр URL-адреси, а зовсім не інтерпретувати як URL-адресу. Тоді як ви хочете, щоб він просто перетворив одну невелику частину URL-адреси.
Стівен Холт

Відповіді:


303

Клас java.net.URI може допомогти; в документації URL-адреси, яку ви знайдете

Зауважте, що клас URI виконує втечу своїх компонентних полів за певних обставин. Рекомендованим способом управління кодуванням та декодуванням URL-адрес є використання URI

Використовуйте одного з конструкторів з більш ніж одним аргументом, наприклад:

URI uri = new URI(
    "http", 
    "search.barnesandnoble.com", 
    "/booksearch/first book.pdf",
    null);
URL url = uri.toURL();
//or String request = uri.toString();

(конструктор одноаргументів URI НЕ уникає незаконних символів)


Лише нелегальні символи можуть уникнути вищевказаного коду - він НЕ уникає символів, що не належать до ASCII (див. Коментар fatih). Метод може бути використаний , щоб отримати рядок тільки з US-ASCII символів:
toASCIIString

URI uri = new URI(
    "http", 
    "search.barnesandnoble.com", 
    "/booksearch/é",
    null);
String request = uri.toASCIIString();

Для URL-адреси з таким запитом http://www.google.com/ig/api?weather=São Pauloвикористовуйте 5-параметричну версію конструктора:

URI uri = new URI(
        "http", 
        "www.google.com", 
        "/ig/api",
        "weather=São Paulo",
        null);
String request = uri.toASCIIString();

13
Зауважте, що клас URI, згаданий тут, походить від "org.apache.commons.httpclient.URI" не "java.net", "java.net" не URI не приймає незаконних символів, якщо ви не будете використовувати конструктори, які будують URL-адресу зі своїх компонентів, як, наприклад, згадується в коментарі Метта нижче
Мохамед Фарамаві

7
@Mohamed: клас, про який я згадував і який використовував для тестування, насправді такий java.net.URI : він працював ідеально (Java 1.6). Я б зазначив цілком кваліфіковане ім'я класу, якби це не стандартний Java, а посилання на документацію java.net.URI. І, за коментарем Sudhakar, це вирішило проблему, не включивши жодної "бібліотеки"!
користувач85421

1
URI uri = новий URI ("http", "search.barnesandnoble.com", "/ booksearch / é", null); Чи не вдається правильно втекти з цим зразком? Цьому слід було б уникнути% втечі
fmucar

@fatih - це правильно, дякую! Зазвичай це не повинно бути проблемою, але є просте рішення - майже таке, як я писав раніше. Див. Другу редакцію.
користувач85421

@Carlos Thx для редагування. Тепер він робить втечу, але не правильний втечу. Слід додати% до значення HEX char для
параметок

91

Будьте попереджені, що більшість відповідей, наведених вище, НЕПРАВНІ

URLEncoderКлас, незважаючи на це назва, не те , що повинно бути тут. Прикро, що Сун назвав цей клас так прикро. URLEncoderпризначений для передачі даних як параметрів, а не для кодування самої URL-адреси.

Іншими словами, "http://search.barnesandnoble.com/booksearch/first book.pdf"це URL-адреса. Параметри були б, наприклад, "http://search.barnesandnoble.com/booksearch/first book.pdf?parameter1=this&param2=that". Параметри - це те, що ви б використовували URLEncoder.

Наступні два приклади підкреслюють відмінності між ними.

Нижче наведено неправильні параметри, згідно стандарту HTTP. Зверніть увагу, що амперсанд (&) і плюс (+) закодовані неправильно.

uri = new URI("http", null, "www.google.com", 80, 
"/help/me/book name+me/", "MY CRZY QUERY! +&+ :)", null);

// URI: http://www.google.com:80/help/me/book%20name+me/?MY%20CRZY%20QUERY!%20+&+%20:)

Далі буде створено правильні параметри із запитом, правильно зашифрованим. Зверніть увагу на пробіли, розширення та позначки плюс.

uri = new URI("http", null, "www.google.com", 80, "/help/me/book name+me/", URLEncoder.encode("MY CRZY QUERY! +&+ :)", "UTF-8"), null);

// URI: http://www.google.com:80/help/me/book%20name+me/?MY+CRZY+QUERY%2521+%252B%2526%252B+%253A%2529

2
Правильно, конструктор URI вже кодує рядок запитів відповідно до документації docs.oracle.com/javase/1.4.2/docs/api/java/net/… , java.lang.String, java.lang.String, int , java.lang.String, java.lang.String, java.lang.String)
madoke

8
@Draemon Відповідь правильна, але використовує рядок запиту нечасто; більш нормальним прикладом може бути query = URLEncoder.encode(key) + "=" + URLEncoder.encode(value). Документи просто говорять, що "цитується будь-який символ, який не є законним символом URI".
тс.

1
Я згоден з Меттом тут. Якщо ви введете цю URL-адресу: " google.com/help/me/book ім'я + я /? МОЙ КРИЗЬКИЙ ЗАПИТАННЯ! + & + :)" у веб-переглядачі, він автоматично кодує пробіли, але "&" використовується як значення запиту роздільник і "+" втрачені.
arcot

80

Я збираюся додати тут одну пропозицію, спрямовану на користувачів Android. Це можна зробити, щоб уникнути необхідності отримати будь-які зовнішні бібліотеки. Крім того, усі рішення пошуку / заміни символів, запропоновані в деяких відповідях вище, є небезпечними і їх слід уникати.

Спробуйте:

String urlStr = "http://abc.dev.domain.com/0007AC/ads/800x480 15sec h.264.mp4";
URL url = new URL(urlStr);
URI uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef());
url = uri.toURL();

Ви можете бачити, що в цій конкретній URL-адресі мені потрібно кодувати ці пробіли, щоб я міг використовувати його для запиту.

Це використовує кілька функцій, доступних для вас в класах Android. По-перше, клас URL може розбити URL-адресу на належні компоненти, тому немає необхідності виконувати роботу з пошуку / заміни рядків. По-друге, цей підхід використовує особливість класу URI, що належним чином виходить з компонентів, коли ви створюєте URI через компоненти, а не з одного рядка.

Краса такого підходу полягає в тому, що ви можете взяти будь-яку дійсну URL-адресу і працювати з нею, не потребуючи жодних спеціальних знань про неї.


3
Хороший підхід, але я хотів би зазначити, що цей код не перешкоджає подвійному кодуванню , наприклад,% 20 закодовано в% 2520. Відповідь Скотта від цього не страждає.
nattster

2
Він не може впоратися #.
Елстон

Або якщо ви просто хочете зробити цитування шляхів: новий URI (null, null, "/ шлях з пробілами", null, null) .toString ()
user1050755

1
@Stallman Якщо ім'я вашого файлу містить #, клас URL переведе його у "ref" (еквівалент "фрагменту" у класі URI). Ви можете визначити, чи повертає URL.getRef () щось, що може розглядатися як частина шляху, і передати URL.getPath () + "#" + URL.getRef () як параметр "шлях", а нуль як "фрагмент" "параметр конструктора параметрів URI класу 7. За замовчуванням рядок після # трактується як посилання (або як прив’язка).
gouessej

49

рішення, яке я розробив і набагато стабільніше, ніж будь-який інший:

public class URLParamEncoder {

    public static String encode(String input) {
        StringBuilder resultStr = new StringBuilder();
        for (char ch : input.toCharArray()) {
            if (isUnsafe(ch)) {
                resultStr.append('%');
                resultStr.append(toHex(ch / 16));
                resultStr.append(toHex(ch % 16));
            } else {
                resultStr.append(ch);
            }
        }
        return resultStr.toString();
    }

    private static char toHex(int ch) {
        return (char) (ch < 10 ? '0' + ch : 'A' + ch - 10);
    }

    private static boolean isUnsafe(char ch) {
        if (ch > 128 || ch < 0)
            return true;
        return " %$&+,/:;=?@<>#%".indexOf(ch) >= 0;
    }

}

3
що також вимагає від вас розбити URL на частини. Комп'ютер не може знати, яку частину URL-коду потрібно кодувати. Дивіться мою вище
редакцію

4
@fmucar Дякую за цей фрагмент коду! Слід зазначити, що це не UTF-8. Щоб отримати UTF-8, просто попередньо обробіть вхід String utf8Input = new String(Charset.forName("UTF-8").encode(input).array());(взятий звідси )
letmaik

1
Це рішення насправді також кодує частину "http: //" в "http% 3A% 2F% 2F", саме цього намагалося уникнути початкового питання.
Бенджамін Піет

2
Ви передаєте лише те, що потрібно кодувати, а не всю URL-адресу. Немає можливості пропустити один цілий рядок URL-адреси та очікувати правильного кодування. У всіх випадках вам потрібно розбити URL на його логічні частини.
fmucar

2
У мене виникли проблеми з цією відповіддю, оскільки він не кодує небезпечні характеристики UTF-8 .. Хоча це може залежати від програми однорангових програм.
Тарншаф

36

Якщо у вас є URL, ви можете передати url.toString () у цей метод. Спочатку декодуйте, щоб уникнути подвійного кодування (наприклад, кодування простору призводить до% 20, а кодування знаку відсотка призводить до% 25, ​​тому подвійне кодування перетворить простір у% 2520). Потім скористайтеся URI, як пояснено вище, додавши всі частини URL-адреси (щоб ви не скидали параметри запиту).

public URL convertToURLEscapingIllegalCharacters(String string){
    try {
        String decodedURL = URLDecoder.decode(string, "UTF-8");
        URL url = new URL(decodedURL);
        URI uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef()); 
        return uri.toURL(); 
    } catch (Exception ex) {
        ex.printStackTrace();
        return null;
    }
}

1
URLDecoder.decode (рядок, "UTF-8") не вдається з IllegalArgumentException, коли ви передаєте рядок як " google.co.in/search?q=123%!123 ". Це дійсна URL-адреса. Я думаю, цей API не працює, коли% використовується як дані замість символу кодування.
MediumOne

26

Так, кодування URL буде кодувати цей рядок, щоб він був належним чином переданий у URL-адресі до кінцевого пункту призначення. Наприклад, у вас не було http://stackoverflow.com?url=http://yyy.com . UrlEncoding параметру буде виправити це значення параметра.

Тож у мене є два варіанти для вас:

  1. Чи маєте ви доступ до шляху, окремо від домену? Якщо так, можливо, ви зможете просто UrlEncode шлях. Однак якщо це не так, то варіант 2 може бути для вас.

  2. Отримайте commons-httpclient-3.1. Це клас URIUtil:

    System.out.println (URIUtil.encodePath (" http://example.com/x y", "ISO-8859-1"));

Це виведе саме те, що ви шукаєте, оскільки воно лише кодує частину шляху URI.

FYI, для роботи під час виконання цього методу вам знадобиться commons-codec та commons-logging.


Sidenote apache commons перестав підтримувати URIUtil у 4.x гілках, мабуть, рекомендуючи замість цього використовувати клас URI JDK. Просто означає, що ви повинні розірвати рядок самостійно.
Ніколі

2) Точно також пропонується тут stackoverflow.com/questions/5330104/… Я також використовував URIUtilрішення
До

11

Нитчіпкінг: рядок, що містить символ пробілу, за визначенням не є URI. Тож, що ви шукаєте, це код, який реалізує перехід URI, визначений у розділі 2.1 RFC 3986 .


Нам потрібно відповіді "як", а не "що".
shinzou

11

На жаль, org.apache.commons.httpclient.util.URIUtilзастаріле, а replacement org.apache.commons.codec.net.URLCodecкодування дій підходить для публікацій форми, а не у фактичних URL-адресах. Тому мені довелося записати власну функцію, яка робить один компонент (не підходить для цілих рядків запитів, які мають? 'І &' s)

public static String encodeURLComponent(final String s)
{
  if (s == null)
  {
    return "";
  }

  final StringBuilder sb = new StringBuilder();

  try
  {
    for (int i = 0; i < s.length(); i++)
    {
      final char c = s.charAt(i);

      if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z')) ||
          ((c >= '0') && (c <= '9')) ||
          (c == '-') ||  (c == '.')  || (c == '_') || (c == '~'))
      {
        sb.append(c);
      }
      else
      {
        final byte[] bytes = ("" + c).getBytes("UTF-8");

        for (byte b : bytes)
        {
          sb.append('%');

          int upper = (((int) b) >> 4) & 0xf;
          sb.append(Integer.toHexString(upper).toUpperCase(Locale.US));

          int lower = ((int) b) & 0xf;
          sb.append(Integer.toHexString(lower).toUpperCase(Locale.US));
        }
      }
    }

    return sb.toString();
  }
  catch (UnsupportedEncodingException uee)
  {
    throw new RuntimeException("UTF-8 unsupported!?", uee);
  }
}

Давай, має бути бібліотека, яка це робить.
shinzou

9

URLEncoding може добре кодувати URL-адреси HTTP, як ви, на жаль, виявили. Рядок, який ви передали, " http://search.barnesandnoble.com/booksearch/first book.pdf", був правильно та повністю закодований у форму, кодовану URL-адресою. Ви можете передати всю довгу нитку gobbledigook, яку ви отримали назад як параметр у URL-адресі, і її можна було б декодувати назад у саме ту строку, яку ви передали.

Здається, ви хочете зробити щось трохи інше, ніж передавати всю URL-адресу як параметр. З того, що я збираю, ви намагаєтесь створити пошукову URL-адресу, що виглядає як " http://search.barnesandnoble.com/booksearch/wwhatTheUserPassesIn ". Єдине, що потрібно кодувати - це біт "whatTheUserPassesIn", тому, можливо, все, що вам потрібно зробити, - це щось подібне:

String url = "http://search.barnesandnoble.com/booksearch/" + 
       URLEncoder.encode(userInput,"UTF-8");

Це повинно створити для вас щось більш вірне.


17
Це замінить пробіли в userInput на "+". Плакат потребує їх замінити на "% 20".
vocaro

@vocaro: це дуже хороший момент. URLEncoder виходить так, як аргументи є параметрами запиту, як і решта URL-адреси.
Брендон Ярбро

9

Якщо хтось не хоче додавати залежність до свого проекту, ці функції можуть бути корисними.

Ми передаємо сюди частину "шляху" своєї URL-адреси. Ви, ймовірно, не хочете передавати повну URL-адресу як параметр (рядки запиту потребують різних скатів тощо).

/**
 * Percent-encodes a string so it's suitable for use in a URL Path (not a query string / form encode, which uses + for spaces, etc)
 */
public static String percentEncode(String encodeMe) {
    if (encodeMe == null) {
        return "";
    }
    String encoded = encodeMe.replace("%", "%25");
    encoded = encoded.replace(" ", "%20");
    encoded = encoded.replace("!", "%21");
    encoded = encoded.replace("#", "%23");
    encoded = encoded.replace("$", "%24");
    encoded = encoded.replace("&", "%26");
    encoded = encoded.replace("'", "%27");
    encoded = encoded.replace("(", "%28");
    encoded = encoded.replace(")", "%29");
    encoded = encoded.replace("*", "%2A");
    encoded = encoded.replace("+", "%2B");
    encoded = encoded.replace(",", "%2C");
    encoded = encoded.replace("/", "%2F");
    encoded = encoded.replace(":", "%3A");
    encoded = encoded.replace(";", "%3B");
    encoded = encoded.replace("=", "%3D");
    encoded = encoded.replace("?", "%3F");
    encoded = encoded.replace("@", "%40");
    encoded = encoded.replace("[", "%5B");
    encoded = encoded.replace("]", "%5D");
    return encoded;
}

/**
 * Percent-decodes a string, such as used in a URL Path (not a query string / form encode, which uses + for spaces, etc)
 */
public static String percentDecode(String encodeMe) {
    if (encodeMe == null) {
        return "";
    }
    String decoded = encodeMe.replace("%21", "!");
    decoded = decoded.replace("%20", " ");
    decoded = decoded.replace("%23", "#");
    decoded = decoded.replace("%24", "$");
    decoded = decoded.replace("%26", "&");
    decoded = decoded.replace("%27", "'");
    decoded = decoded.replace("%28", "(");
    decoded = decoded.replace("%29", ")");
    decoded = decoded.replace("%2A", "*");
    decoded = decoded.replace("%2B", "+");
    decoded = decoded.replace("%2C", ",");
    decoded = decoded.replace("%2F", "/");
    decoded = decoded.replace("%3A", ":");
    decoded = decoded.replace("%3B", ";");
    decoded = decoded.replace("%3D", "=");
    decoded = decoded.replace("%3F", "?");
    decoded = decoded.replace("%40", "@");
    decoded = decoded.replace("%5B", "[");
    decoded = decoded.replace("%5D", "]");
    decoded = decoded.replace("%25", "%");
    return decoded;
}

І тести:

@Test
public void testPercentEncode_Decode() {
    assertEquals("", percentDecode(percentEncode(null)));
    assertEquals("", percentDecode(percentEncode("")));

    assertEquals("!", percentDecode(percentEncode("!")));
    assertEquals("#", percentDecode(percentEncode("#")));
    assertEquals("$", percentDecode(percentEncode("$")));
    assertEquals("@", percentDecode(percentEncode("@")));
    assertEquals("&", percentDecode(percentEncode("&")));
    assertEquals("'", percentDecode(percentEncode("'")));
    assertEquals("(", percentDecode(percentEncode("(")));
    assertEquals(")", percentDecode(percentEncode(")")));
    assertEquals("*", percentDecode(percentEncode("*")));
    assertEquals("+", percentDecode(percentEncode("+")));
    assertEquals(",", percentDecode(percentEncode(",")));
    assertEquals("/", percentDecode(percentEncode("/")));
    assertEquals(":", percentDecode(percentEncode(":")));
    assertEquals(";", percentDecode(percentEncode(";")));

    assertEquals("=", percentDecode(percentEncode("=")));
    assertEquals("?", percentDecode(percentEncode("?")));
    assertEquals("@", percentDecode(percentEncode("@")));
    assertEquals("[", percentDecode(percentEncode("[")));
    assertEquals("]", percentDecode(percentEncode("]")));
    assertEquals(" ", percentDecode(percentEncode(" ")));

    // Get a little complex
    assertEquals("[]]", percentDecode(percentEncode("[]]")));
    assertEquals("a=d%*", percentDecode(percentEncode("a=d%*")));
    assertEquals(")  (", percentDecode(percentEncode(")  (")));
    assertEquals("%21%20%2A%20%27%20%28%20%25%20%29%20%3B%20%3A%20%40%20%26%20%3D%20%2B%20%24%20%2C%20%2F%20%3F%20%23%20%5B%20%5D%20%25",
                    percentEncode("! * ' ( % ) ; : @ & = + $ , / ? # [ ] %"));
    assertEquals("! * ' ( % ) ; : @ & = + $ , / ? # [ ] %", percentDecode(
                    "%21%20%2A%20%27%20%28%20%25%20%29%20%3B%20%3A%20%40%20%26%20%3D%20%2B%20%24%20%2C%20%2F%20%3F%20%23%20%5B%20%5D%20%25"));

    assertEquals("%23456", percentDecode(percentEncode("%23456")));

}

Дякую за це, але що мені потрібно зробити, щоб кодувати пробіл -> використовувати% 20 замість цього відповідно до вашого прикладу?
N00b Pr0grammer

Оновлено для обліку пробілів як% 20
Куга

7

Проблема все ще існує, якщо у вашій URL-адресі закодовано "/" (% 2F).

RFC 3986 - Розділ 2.2 говорить: "Якщо дані для компонента URI суперечать призначенню зарезервованого символу як роздільника, то суперечливі дані повинні бути закодовані у відсотках до формування URI". (RFC 3986 - Розділ 2.2)

Але з Tomcat є проблема:

http://tomcat.apache.org/security-6.html - Виправлено в Apache Tomcat 6.0.10

важливо: обхід каталогів CVE-2007-0450

Tomcat дозволяє "\", "% 2F" та "% 5C" [...].

Наступні системні властивості Java були додані в Tomcat, щоб забезпечити додатковий контроль поводження з роздільниками шляху в URL-адресах (обидва варіанти за замовчуванням до false):

  • org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH: true | false
  • org.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH: true | false

Через неможливість гарантувати, що Tomcat обробляє всі URL-адреси, оскільки вони є на проксі-серверах, Tomcat завжди повинен бути захищений так, як ніби не використовувався проксі-обмежувальний контекстний доступ.

Впливає: 6.0.0-6.0.9

Тож якщо у вас є URL з символом% 2F, Tomcat повертає: "400 Недійсний URI: noSlash"

Ви можете перемикати помилку в сценарії запуску Tomcat:

set JAVA_OPTS=%JAVA_OPTS% %LOGGING_CONFIG%   -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true 

7

Я читав попередні відповіді, щоб написати свій власний метод, тому що я не міг щось нормально працювати, використовуючи рішення попередніх відповідей, для мене це виглядає добре, але якщо ви можете знайти URL, який не працює з цим, будь ласка, повідомте мене про це.

public static URL convertToURLEscapingIllegalCharacters(String toEscape) throws MalformedURLException, URISyntaxException {
            URL url = new URL(toEscape);
            URI uri = new URI(url.getProtocol(), url.getUserInfo(), url.getHost(), url.getPort(), url.getPath(), url.getQuery(), url.getRef());
            //if a % is included in the toEscape string, it will be re-encoded to %25 and we don't want re-encoding, just encoding
            return new URL(uri.toString().replace("%25", "%"));
}

4

Я згоден з Меттом. Дійсно, я ніколи не бачив, щоб це було добре пояснено в навчальних посібниках, але одне питання полягає в тому, як кодувати URL-шлях, а зовсім інший - як кодувати параметри, які додаються до URL-адреси (частина запиту, позаду "? "символ". Вони використовують аналогічне кодування, але не те саме.

Спеціально для кодування символу білого простору. Шлях URL-адреси потребує кодування як% 20, тоді як частина запиту дозволяє% 20, а також знак "+". Найкраща ідея - перевірити його на нашому веб-сервері за допомогою веб-браузера.

В обох випадках Я ВЖЕ кодував би КОМПОНЕНТ КОМПОНЕНТОМ , а не цілим рядком. Дійсно, URLEncoder дозволяє це для частини запиту. Для частини шляху ви можете використовувати URI класу, хоча в цьому випадку він запитує весь рядок, а не один компонент.

У всякому разі, я вважаю, що найкращим способом уникнути цих проблем є використання особистого безконфліктного дизайну. Як? Наприклад, я ніколи б не називав каталоги чи параметри, використовуючи інші символи, ніж aZ, AZ, 0-9 та _. Таким чином, єдина потреба полягає в кодуванні значення кожного параметра, оскільки воно може надходити з вводу користувача, а використовувані символи невідомі.


2
зразок коду з використанням URL-адреси у запитанні було б добре ввести свою відповідь
Мартін Серрано


3

Ви також можете використовувати GUAVAта уникнути шляху: UrlEscapers.urlFragmentEscaper().escape(relativePath)


2

На додаток до відповіді Карлоса Гюбергера: якщо потрібний інший, ніж стандартний (80), слід використовувати конструктор 7 парам:

URI uri = new URI(
        "http",
        null, // this is for userInfo
        "www.google.com",
        8080, // port number as int
        "/ig/api",
        "weather=São Paulo",
        null);
String request = uri.toASCIIString();

2

Я взяв вміст вище і трохи змінив його. Спочатку мені подобається позитивна логіка, і я подумав, що HashSet може дати кращу ефективність, ніж деякі інші параметри, наприклад, пошук за допомогою String. Хоча я не впевнений, чи коштує штраф за автобоксинг, але якщо компілятор оптимізує ASCII символи, то вартість боксу буде низькою.

/***
 * Replaces any character not specifically unreserved to an equivalent 
 * percent sequence.
 * @param s
 * @return
 */
public static String encodeURIcomponent(String s)
{
    StringBuilder o = new StringBuilder();
    for (char ch : s.toCharArray()) {
        if (isSafe(ch)) {
            o.append(ch);
        }
        else {
            o.append('%');
            o.append(toHex(ch / 16));
            o.append(toHex(ch % 16));
        }
    }
    return o.toString();
}

private static char toHex(int ch)
{
    return (char)(ch < 10 ? '0' + ch : 'A' + ch - 10);
}

// https://tools.ietf.org/html/rfc3986#section-2.3
public static final HashSet<Character> UnreservedChars = new HashSet<Character>(Arrays.asList(
        'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
        'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
        '0','1','2','3','4','5','6','7','8','9',
        '-','_','.','~'));
public static boolean isSafe(char ch)
{
    return UnreservedChars.contains(ch);
}

1

Використовуйте таке стандартне рішення Java (проходить близько 100 тестів, наданих тестами веб-платформи ):

0. Перевірте, чи URL-адреса вже закодована .

1. Розділіть URL-адресу на структурні частини. Використовуйте java.net.URL для цього.

2. Кодувати кожну структурну частину належним чином!

3. Використовуйте IDN.toASCII(putDomainNameHere)для кодування Punycode ім'я хоста!

4. Використовуйте java.net.URI.toASCIIString()для кодування відсотків NICK, кодованого NFC - (краще було б NFKC!).

Дізнайтеся більше тут: https://stackoverflow.com/a/49796882/1485527


0

Я створив новий проект, який допоможе побудувати URL-адреси HTTP. Бібліотека автоматично кодує URL-адреси сегментів шляху та параметрів запиту.

Ви можете переглянути джерело та завантажити двійковий файл за посиланням https://github.com/Widen/urlbuilder

Приклад URL-адреси цього питання:

new UrlBuilder("search.barnesandnoble.com", "booksearch/first book.pdf").toString()

виробляє

http://search.barnesandnoble.com/booksearch/first%20book.pdf


0

У мене була така ж проблема. Вирішили це шляхом відстеження:

android.net.Uri.encode(urlString, ":/");

Він кодує рядок, але пропускає ":" і "/".


0

я використовую це

org.apache.commons.text.StringEscapeUtils.escapeHtml4("my text % & < >");

додайте цю залежність

 <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-text</artifactId>
        <version>1.8</version>
    </dependency>

-2

Я розвиваю бібліотеку, яка служить цій цілі: галіматії . Він аналізує URL так само, як це роблять веб-браузери. Тобто, якщо URL-адреса працює в браузері, вона буде правильно проаналізована галіматіями .

В цьому випадку:

// Parse
io.mola.galimatias.URL.parse(
    "http://search.barnesandnoble.com/booksearch/first book.pdf"
).toString()

Дамо вам: http://search.barnesandnoble.com/booksearch/first%20book.pdf. Звичайно, це найпростіший випадок, але він буде працювати з чим завгодно, за його межами java.net.URI.

Перевірити це можна за посиланням: https://github.com/smola/galimatias


-3

Ви можете використовувати таку функцію. Доповніть і модифікуйте його відповідно до ваших потреб:

/**
     * Encode URL (except :, /, ?, &, =, ... characters)
     * @param url to encode
     * @param encodingCharset url encoding charset
     * @return encoded URL
     * @throws UnsupportedEncodingException
     */
    public static String encodeUrl (String url, String encodingCharset) throws UnsupportedEncodingException{
            return new URLCodec().encode(url, encodingCharset).replace("%3A", ":").replace("%2F", "/").replace("%3F", "?").replace("%3D", "=").replace("%26", "&");
    }

Приклад використання:

String urlToEncode = ""http://www.growup.com/folder/intérieur-à_vendre?o=4";
Utils.encodeUrl (urlToEncode , "UTF-8")

Результат: http://www.growup.com/folder/int%C3%A9rieur-%C3%A0_vendre?o=4


1
Ця відповідь є неповною без URLCodec.
Маркіз Лорн

upvote для .replace () ланцюга, це не ідеально, але цього достатньо для основних випадків спеціального використання
svarog

-5

String url = "" http://search.barnesandnoble.com/booksearch/ ;

Я думаю, це буде постійним, і лише ім'я файлу змінюється динамічно, тому отримайте ім'я файлу

Ім'я файлу рядка; // отримати ім'я файлу

Рядок urlEnc = url + fileName.replace ("", "% 20");


2
Що з усіма іншими незаконними персонажами?
Маркіз Лорн

-7

Як щодо:

public String UrlEncode (Рядок в_) {

String retVal = "";

try {
    retVal = URLEncoder.encode(in_, "UTF8");
} catch (UnsupportedEncodingException ex) {
    Log.get().exception(Log.Level.Error, "urlEncode ", ex);
}

return retVal;

}


URLEncoder не можна використовувати для уникнення недійсних символів URL. Тільки для кодування форм.
Арчер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.