Кодирование URL-адреса HTTP в Java
мое автономное приложение 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 не предназначен для кодирования http-адресов... JavaDoc говорит " класс утилиты для HTML кодирование формы"... Есть ли другой способ сделать это?
24 ответа:
класс 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 (см. комментарий Фатиха).
ЭлементtoASCIIStringметод может быть использован для получения строки только с US-ASCII символов: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();
пожалуйста, имейте в виду, что большинство ответов выше неверны.
The
URLEncoderкласс, несмотря на это имя, это не то, что должно быть здесь. Жаль, что Солнце назвало этот класс так раздражающе.URLEncoderпредназначен для передачи данных в качестве параметров, а не для кодирования самого URL-адреса.другими словами,
"http://search.barnesandnoble.com/booksearch/first book.pdf"- это URL. Параметры будут, например,"http://search.barnesandnoble.com/booksearch/first book.pdf?parameter1=this¶m2=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
Я собираюсь добавить одно предложение здесь, направленное на пользователей 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-адресе мне нужно закодировать эти пробелы, чтобы я мог использовать его для запроса.
Это использует в своих интересах пару функции, доступные для вас в классах для Андроид. Во-первых, класс URL может разбить url на соответствующие компоненты, поэтому вам не нужно выполнять поиск/замену строк. Во-вторых, этот подход использует функцию класса URI правильного экранирования компонентов при создании URI через компоненты, а не из одной строки.
красота этого подхода заключается в том, что вы можете взять любую допустимую строку url и заставить ее работать без каких-либо специальных знаний об этом себе.
решение я разработал и гораздо более стабильным, чем любой другой:
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; } }
если у вас есть 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; } }
да кодировка URL будет кодировать эту строку, чтобы она была правильно передана в url-адрес конечного пункта назначения. Например, вы не могли иметь http://stackoverflow.com?url=http://yyy.com. UrlEncoding параметр будет исправить это значение параметра.
У меня есть два варианта для вас:
есть ли у вас доступ к пути отдельно от домена? Если это так, вы можете просто UrlEncode путь. Однако, если это не тогда Вариант 2 может быть для вас.
получить commons-httpclient-3.1. Это имеет класс URIUtil:
система.из.метод println(URIUtil.encodePath("http://example.com/x г", "ИСО-8859-1"));
это выведет именно то, что вы ищете, так как он будет кодировать только часть пути URI.
FYI, вам понадобится commons-codec и commons-logging для работы этого метода во время выполнения.
Nitpicking: строка, содержащая символ пробела по определению не является URI. Так что вы ищете код, который реализует URI escaping, определенный в раздел 2.1 RFC 3986.
к сожалению,
org.apache.commons.httpclient.util.URIUtilявляется устаревшим, аreplacement org.apache.commons.codec.net.URLCodecкодирование не подходит для форма сообщения, а не в фактический URL-адрес. Поэтому мне пришлось написать свою собственную функцию, которая делает один компонент (не подходит для всей строки запроса ?Ы и &'с)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); } }
URLEncoding может кодировать http-адреса просто отлично, как вы, к сожалению, обнаружили. Строка, которую вы передали,"http://search.barnesandnoble.com/booksearch/first книга.pdf", был правильно и полностью закодирован в URL-кодированную форму. Вы можете передать всю эту длинную строку gobbledigook, которую вы вернули в качестве параметра в URL-адресе, и ее можно декодировать обратно в точно переданную строку.
похоже, вы хотите сделать что-то немного отличается от передачи всего URL-адреса в качестве параметра. Из того, что я понял, вы пытаетесь создать URL-адрес поиска, который выглядит как"http://search.barnesandnoble.com/booksearch/whateverTheUserPassesIn". единственное, что вам нужно кодировать, это бит" whateverTheUserPassesIn", поэтому, возможно, все, что вам нужно сделать, это что-то вроде этого:
String url = "http://search.barnesandnoble.com/booksearch/" + URLEncoder.encode(userInput,"UTF-8");Это должно привести к чему-то более значимому для вас.
все еще существует проблема, если у вас есть закодированный "/" (%2F) в URL-адресе.
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.апаш.кот.утиль.баф.UDecoder.ALLOW_ENCODED_SLASH: true / false
- org.апаш.Каталина.соединитель.Койотадаптер.ALLOW_BACKSLASH: true / false
из-за невозможности гарантировать что все URL-адреса обрабатываются Tomcat как они находятся в прокси-серверах, 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
Я прочитал предыдущие ответы, чтобы написать свой собственный метод, потому что я не мог иметь что-то правильно работает с помощью решения предыдущих ответов, это выглядит хорошо для меня, но если вы можете найти 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", "%")); }
Если кто-то не хочет добавлять зависимость к своему проекту, эти функции могут быть полезны.
мы передаем часть "путь" нашего 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"))); }
Я согласен с Мэттом. Действительно, я никогда не видел, чтобы это хорошо объяснялось в учебниках, но один вопрос заключается в том, как кодировать путь URL, а совсем другой-как кодировать параметры, которые добавляются к URL (часть запроса, за "?" символ.) Они используют подобную кодировку, но не то же самое.
специально для кодирования символа пробела. Путь URL-адреса должен быть закодирован как %20, тогда как часть запроса позволяет %20, а также знак"+". Лучшая идея проверьте это сами против нашего веб-сервера, используя веб-браузер.
в обоих случаях, я всегда будет кодировать КОМПОНЕНТ ЗА КОМПОНЕНТОМ, не всю строку. Действительно, средств кодирования позволяет, что для части запросов. Для части пути вы можете использовать класс URI, хотя в этом случае он запрашивает всю строку, а не один компонент.
во всяком случае, я верю, что лучший способ избежать этих проблем-использовать личный неконфликтный дизайн. как? Например, я никогда не буду называть каталоги или параметры, используя другие символы, кроме a-Z, A-Z, 0-9 и _ . Таким образом, необходимо только кодировать значение каждого параметра, так как оно может поступать из пользовательского ввода, а используемые символы неизвестны.
может, попробовать UriUtils в org.springframework.сеть.утиль
UriUtils.encodeUri(input, "UTF-8")
вы также можете использовать
GUAVAи путь escaper:UrlEscapers.urlFragmentEscaper().escape(relativePath)
в дополнение к ответу Карлоса Хойбергера: если требуется что-то отличное от значения по умолчанию (80), следует использовать конструктор 7 param:
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();
Я создал новый проект, чтобы помочь построить http url. Библиотека будет автоматически 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
У меня была та же проблема. Решил это путем unsing:
android.net.Uri.encode(urlString, ":/");кодирует строку, но пропускает ":" и "/".
1. разделить строку URL на структурные части. Используйте
java.net.URLдля него.2. кодировать каждую структурную часть правильно!
3. использовать
IDN.toASCII(putDomainNameHere)до Punycode кодировать имя хоста!4. использовать
java.net.URI.toASCIIString()для процентного кодирования, NFC кодируется unicode- (лучше бы NFKC!). Для получения дополнительной информации см.:Как правильно закодировать этот URLURL url= new URL("http://search.barnesandnoble.com/booksearch/first book.pdf); URI uri = new URI(url.getProtocol(), url.getUserInfo(), IDN.toASCII(url.getHost()), url.getPort(), url.getPath(), url.getQuery(), url.getRef()); String correctEncodedURL=uri.toASCIIString(); System.out.println(correctEncodedURL);печать
http://search.barnesandnoble.com/booksearch/first%20book.pdf
Я взял над содержимым и изменил его вокруг немного. Сначала мне нравится положительная логика, и я подумал, что хэш-набор может дать лучшую производительность, чем некоторые другие параметры, такие как поиск по строке. Хотя, я не уверен, что штраф автобоксинга стоит того, но если компилятор оптимизирует для символов 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); }
Я разрабатываю библиотеку, которая служит этой цели: galimatias. Он анализирует URL-адрес так же, как это делают веб-браузеры. То есть, если URL-адрес работает в браузере, он будет правильно обработан galimatias.
В этом случае:
// 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
вы можете использовать такую функцию. Заполните и измените его в соответствии с вашими потребностями:
/** * 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
строка url=""http://search.barnesandnoble.com/booksearch/;
Это будет постоянным я думаю, и только имя файла изменяется dyamically так получить имя файла
строковое имя файла; // получить имя файла
строка urlEnc=url+имя файла.заменить(" ","%20");