example - Codificación de la dirección URL de HTTP en Java
urlencoder java example (24)
Mi aplicación Java independiente obtiene una URL (que apunta a un archivo) del usuario y necesito golpearla y descargarla. El problema al que me enfrento es que no puedo codificar correctamente la dirección URL de HTTP ...
Ejemplo:
URL: http://search.barnesandnoble.com/booksearch/first book.pdf
java.net.URLEncoder.encode(url.toString(), "ISO-8859-1");
me devuelve
http%3A%2F%2Fsearch.barnesandnoble.com%2Fbooksearch%2Ffirst+book.pdf
Pero, lo que quiero es
http://search.barnesandnoble.com/booksearch/first%20book.pdf
(espacio reemplazado por% 20)
Supongo que URLEncoder
no está diseñado para codificar URL de HTTP ... El JavaDoc dice "Clase de utilidad para la codificación de formularios HTML" ... ¿Hay alguna otra forma de hacer esto?
Además de la respuesta de Carlos Heuberger: si se necesita un valor diferente al predeterminado (80), se debe usar el constructor de 7 parámetros:
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();
Desafortunadamente, org.apache.commons.httpclient.util.URIUtil
está en desuso, y el replacement org.apache.commons.codec.net.URLCodec
hace una codificación adecuada para las publicaciones de formularios, no en las URL reales. Así que tuve que escribir mi propia función, que hace un solo componente (no es adecuado para cadenas de consulta completas que tienen? ''S y &'' 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);
}
}
Desarrollo una biblioteca que sirve para este propósito: galimatias . Analiza la URL de la misma manera que los navegadores web. Es decir, si una URL funciona en un navegador, será analizada correctamente por galimatias .
En este caso:
URL 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);
Le dará: http://search.barnesandnoble.com/booksearch/first%20book.pdf
. Por supuesto, este es el caso más simple, pero funcionará con cualquier cosa, mucho más allá de java.net.URI
.
Puede verlo en: galimatias
Estoy de acuerdo con Matt. De hecho, nunca lo he visto bien explicado en los tutoriales, pero una cuestión es cómo codificar la ruta de la URL, y otra muy diferente es cómo codificar los parámetros que se adjuntan a la URL (la parte de consulta, detrás de "? "símbolo). Usan una codificación similar, pero no la misma.
Especialmente para la codificación del carácter de espacio en blanco. La ruta de la URL necesita estar codificada como% 20, mientras que la parte de consulta permite% 20 y también el signo "+". La mejor idea es probarlo por nosotros mismos en nuestro servidor web, utilizando un navegador web.
Para ambos casos, SIEMPRE codificaría COMPONENTE POR COMPONENTE , nunca la cadena completa. De hecho, URLEncoder lo permite para la parte de consulta. Para la parte de la ruta puede usar la clase URI, aunque en este caso solicita la cadena completa, no un solo componente.
De todos modos, creo que la mejor manera de evitar estos problemas es usar un diseño personal no conflictivo. ¿Cómo? Por ejemplo, nunca nombraría directorios o parámetros con otros caracteres que no sean aZ, AZ, 0-9 y _. De esa manera, la única necesidad es codificar el valor de cada parámetro, ya que puede provenir de una entrada del usuario y los caracteres utilizados son desconocidos.
He creado un nuevo proyecto para ayudar a construir URLs HTTP. La biblioteca codificará automáticamente los segmentos de la ruta de codificación y los parámetros de consulta.
Puede ver la fuente y descargar un archivo binario en https://github.com/Widen/urlbuilder
La URL de ejemplo en esta pregunta:
new UrlBuilder("search.barnesandnoble.com", "booksearch/first book.pdf").toString()
produce
http://search.barnesandnoble.com/booksearch/first%20book.pdf
La clase java.net.URI puede ayudar; en la documentación de URL que encuentre
Tenga en cuenta que la clase URI realiza el escape de sus campos componentes en ciertas circunstancias. La forma recomendada de administrar la codificación y decodificación de las URL es usar un URI
Utilice uno de los constructores con más de un argumento, como:
URI uri = new URI(
"http",
"search.barnesandnoble.com",
"/booksearch/first book.pdf",
null);
URL url = uri.toURL();
//or String request = uri.toString();
(el constructor de un solo argumento de URI NO escapa a los caracteres ilegales)
Solo los caracteres ilegales se escapan por el código anterior, NO se escapan de los caracteres no ASCII (vea el comentario de fatih).
El método toASCIIString
se puede usar para obtener una cadena solo con caracteres US-ASCII:
URI uri = new URI(
"http",
"search.barnesandnoble.com",
"/booksearch/é",
null);
String request = uri.toASCIIString();
Para una URL con una consulta como http://www.google.com/ig/api?weather=São Paulo
, use la versión de 5 parámetros del constructor:
URI uri = new URI(
"http",
"www.google.com",
"/ig/api",
"weather=São Paulo",
null);
String request = uri.toASCIIString();
Leí las respuestas anteriores para escribir mi propio método porque no podía tener algo funcionando correctamente usando la solución de las respuestas anteriores, me parece bien, pero si puedes encontrar una URL que no funcione con esto, házmelo saber.
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", "%"));
}
Nitpicking: una cadena que contiene un carácter de espacio en blanco por definición no es un URI. Entonces, lo que está buscando es un código que implemente el escape URI definido en la Sección 2.1 de RFC 3986 .
Puedes usar una función como esta. Completa y modifícalo a tu necesidad:
http://search.barnesandnoble.com/booksearch/first%20book.pdf
Ejemplo de uso:
// Parse
io.mola.galimatias.URL.parse(
"http://search.barnesandnoble.com/booksearch/first book.pdf"
).toString()
El resultado es: http://www.growup.com/folder/int%C3%A9rieur-%C3%A0_vendre?o=4
Qué tal si:
UrlEncode de cadena pública (String in_) {
/**
* 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", "&");
}
}
Sí, la codificación de la URL va a codificar esa cadena para que se transmita correctamente en un url a un destino final. Por ejemplo, no podría tener http://.com?url=http://yyy.com . UrlEncodificar el parámetro arreglaría ese valor de parámetro.
Así que tengo dos opciones para ti:
¿Tienes acceso a la ruta separada del dominio? Si es así, puede simplemente ingresar el URL en la ruta. Sin embargo, si este no es el caso, entonces la opción 2 puede ser para usted.
Obtener commons-httpclient-3.1. Esto tiene una clase URIUtil:
System.out.println (URIUtil.encodePath (" http://example.com/x y", "ISO-8859-1"));
Esto dará como resultado exactamente lo que está buscando, ya que solo codificará la parte de la ruta del URI.
Para su información, necesitará commons-codec y commons-logging para que este método funcione en tiempo de ejecución.
Si alguien no desea agregar una dependencia a su proyecto, estas funciones pueden ser útiles.
Pasamos la parte de ''ruta'' de nuestra URL aquí. Probablemente no quiera pasar la URL completa como parámetro (las cadenas de consulta necesitan escapes diferentes, etc.).
/**
* 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;
}
Y pruebas:
@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")));
}
Si tiene una URL, puede pasar url.toString () a este método. Primero decodifique, para evitar la codificación doble (por ejemplo, la codificación de un espacio resulta en% 20 y la codificación de un signo de porcentaje da como resultado% 25, por lo que la codificación doble convertirá un espacio en% 2520). Luego, use el URI como se explicó anteriormente, agregando todas las partes de la URL (para que no descarte los parámetros de consulta).
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;
}
}
String url = "" http://search.barnesandnoble.com/booksearch/ ;
Supongo que esto será constante y solo los cambios de nombre de archivo se realizarán de forma dinámica, así que obtenga el nombre de archivo
Cadena de nombre de archivo; // obtener el nombre del archivo
String urlEnc = url + fileName.replace ("", "% 20");
Tal vez pueda probar UriUtils en org.springframework.web.util
UriUtils.encodeUri(input, "UTF-8")
También puede usar GUAVA y escaper ruta: UrlEscapers.urlFragmentEscaper().escape(relativePath)
Tenga en cuenta que la mayoría de las respuestas anteriores son INCORRECTAS.
La clase URLEncoder
, a pesar de su nombre, NO es lo que necesita estar aquí. Es desafortunado que Sun haya llamado a esta clase tan molesta. URLEncoder
está diseñado para pasar datos como parámetros, no para codificar la URL en sí.
En otras palabras, "http://search.barnesandnoble.com/booksearch/first book.pdf"
es la URL. Los parámetros serían, por ejemplo, "http://search.barnesandnoble.com/booksearch/first book.pdf?parameter1=this¶m2=that"
. Los parámetros son para lo que utilizarías URLEncoder
.
Los siguientes dos ejemplos resaltan las diferencias entre los dos.
Lo siguiente produce los parámetros incorrectos, de acuerdo con el estándar HTTP. Tenga en cuenta que el símbolo comercial (&) y más (+) están codificados incorrectamente.
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:)
Lo siguiente producirá los parámetros correctos, con la consulta correctamente codificada. Tenga en cuenta los espacios, signos y más marcas.
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
Todavía hay un problema si tienes una "/" codificada (% 2F) en tu URL.
RFC 3986 - La Sección 2.2 dice: "Si los datos para un componente de URI entran en conflicto con el propósito de un personaje reservado como delimitador, entonces los datos en conflicto deben estar codificados en porcentaje antes de que se forme el URI". (RFC 3986 - Sección 2.2)
Pero hay un problema con Tomcat:
http://tomcat.apache.org/security-6.html - Corregido en Apache Tomcat 6.0.10
Importante: Directorio transversal CVE-2007-0450.
Tomcat permite los permisos ''/', ''% 2F'' y ''% 5C'' [...].
Las siguientes propiedades del sistema Java se han agregado a Tomcat para proporcionar un control adicional del manejo de los delimitadores de ruta en las URL (ambas opciones predeterminadas como falsas):
- org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH: true | false
- org.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH: true | false
Debido a la imposibilidad de garantizar que todas las URL sean manejadas por Tomcat ya que están en servidores proxy, Tomcat siempre debe estar protegido como si no se utilizara ningún proxy que restringiera el acceso al contexto.
Afecta: 6.0.0-6.0.9
Entonces, si tienes una URL con el carácter% 2F, Tomcat devuelve: "400 URI no válido: noSlash"
Puede cambiar la corrección de errores en el script de inicio de Tomcat:
set JAVA_OPTS=%JAVA_OPTS% %LOGGING_CONFIG% -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true
Tomé el contenido de arriba y lo cambié un poco. Primero me gusta la lógica positiva, y pensé que un HashSet podría ofrecer un mejor rendimiento que otras opciones, como buscar a través de un String. Aunque no estoy seguro de si la penalización de autoboxing vale la pena, pero si el compilador se optimiza para los caracteres ASCII, el costo del boxeo será bajo.
/***
* 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);
}
URLEncoding puede codificar URLs HTTP muy bien, como lamentablemente has descubierto. La cadena que pasó, " http://search.barnesandnoble.com/booksearch/first book.pdf", se codificó de forma correcta y completa en una forma codificada en URL. Podría pasar toda la larga cadena de gobbledigook que recibió como parámetro en una URL, y podría volver a decodificarse exactamente en la cadena que pasó.
Parece que quieres hacer algo un poco diferente a pasar la URL completa como parámetro. Por lo que deduzco, estás intentando crear una URL de búsqueda que se parece a " http://search.barnesandnoble.com/booksearch/whateverTheUserPassesIn ". Lo único que necesitas para codificar es el bit "whateverTheUserPassesIn", así que quizás todo lo que necesitas hacer es algo como esto:
String url = "http://search.barnesandnoble.com/booksearch/" +
URLEncoder.encode(userInput,"UTF-8");
Eso debería producir algo bastante más válido para ti.
Una solución que desarrollé y mucho más estable que cualquier otra:
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;
}
}
Utilice la siguiente solución estándar de Java (pasa alrededor de 100 de las cajas de prueba provistas por Web Plattform Tests ):
1. Dividir la URL en partes estructurales. Usa java.net.URL
para ello.
2. Codificar cada parte estructural correctamente!
3. Utilice IDN.toASCII(putDomainNameHere)
para codificar el nombre de host en Punycode .
4. Use java.net.URI.toASCIIString()
para codificar en porcentaje, codificado en NFC, (¡mejor sería NFKC!).
Encuentre más aquí: https://.com/a/49796882/1485527
Voy a agregar una sugerencia aquí dirigida a los usuarios de Android. Puede hacer esto, lo que evita tener que obtener bibliotecas externas. Además, todas las soluciones de búsqueda / reemplazo de caracteres sugeridas en algunas de las respuestas anteriores son peligrosas y deben evitarse.
Prueba esto:
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();
Puedes ver que en esta URL en particular, necesito tener esos espacios codificados para poder usarlos para una solicitud.
Esto aprovecha un par de características disponibles para ti en las clases de Android. Primero, la clase de URL puede dividir una url en sus componentes adecuados, por lo que no es necesario que realice ninguna tarea de búsqueda / reemplazo de cadenas. En segundo lugar, este enfoque aprovecha la característica de clase URI de escapar de los componentes correctamente cuando construye un URI a través de componentes en lugar de una sola cadena.
La belleza de este enfoque es que puede tomar cualquier cadena de URL válida y hacer que funcione sin necesidad de un conocimiento especial de la misma.
Yo tuve el mismo problema. Resuelto esto por unsing:
android.net.Uri.encode(urlString, ":/");
Codifica la cadena pero salta ":" y "/".