Codificación de dirección URL HTTP en Java

366

Mi aplicación independiente Java obtiene una URL (que apunta a un archivo) del usuario y necesito presionarla y descargarla. El problema al que me enfrento es que no puedo codificar la dirección URL HTTP correctamente ...

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 URLEncoderque no está diseñado para codificar URL HTTP ... El JavaDoc dice "Clase de utilidad para codificación de formulario HTML" ... ¿Hay alguna otra forma de hacer esto?

suDocker
fuente
El comportamiento es completamente correcto. La codificación de URL es convertir algo en una cadena que se puede pasar de manera segura como un parámetro de URL, y no se interpreta como una URL. Mientras que desea que solo convierta una pequeña parte de la URL.
Stephen Holt

Respuestas:

303

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 URL es usar un URI

Use 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 argumento único de URI NO escapa a caracteres ilegales)


Solo los caracteres ilegales se escapan por el código anterior: NO escapa a los caracteres que no son ASCII (vea el comentario de fatih)
El toASCIIStringmétodo 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();
usuario85421
fuente
13
Tenga en cuenta que la clase de URI mencionada aquí es de "org.apache.commons.httpclient.URI" no "java.net", el "java.net" no URI no acepta los caracteres ilegales, a menos que utilice constructores que construyen URL a partir de sus componentes, como se menciona en el comentario de Matt a continuación
Mohamed Faramawi
77
@Mohamed: la clase que mencioné y usé para probar en realidad es java.net.URI : funcionó perfectamente (Java 1.6). Mencionaría el nombre de clase totalmente calificado si no fuera el estándar de Java y el enlace apunta a la documentación de java.net.URI. Y, por el comentario de Sudhakar, ¡resolvió el problema sin incluir ninguna "biblioteca de bienes comunes"!
user85421
1
URI uri = nuevo URI ("http", "search.barnesandnoble.com", "/ booksearch / é", nulo); ¿No hace correcto escapar con esta muestra? Esto debería haberse escapado con% escapes
fmucar
@fatih - eso es correcto, ¡gracias! Normalmente eso no debería ser un problema, pero hay una solución simple, casi la misma que escribí antes. Ver 2da edición.
user85421
@Carlos Thx para la edición. Ahora se escapa pero no se escapa correctamente. Debe ser la adición de un% al valor hexadecimal de carbón para params Path significa é carbón deben ser convertidos a% e9
fmucar
91

Tenga en cuenta que la mayoría de las respuestas anteriores son INCORRECTAS.

La URLEncoderclase, a pesar de su nombre, NO es lo que necesita estar aquí. Es desafortunado que Sun haya llamado a esta clase tan molestamente. URLEncoderestá destinado a 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&param2=that",. Los parámetros son para lo que usarí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 ampersand (&) y plus (+) 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, los signos y los signos más.

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
Mate
fuente
2
Así es, el constructor de URI ya codifica la cadena de consulta, de acuerdo con la documentación 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 La respuesta es correcta pero usa la cadena de consulta de una manera poco común; Un ejemplo más normal podría ser query = URLEncoder.encode(key) + "=" + URLEncoder.encode(value). Los documentos simplemente dicen que "se cita cualquier carácter que no sea un carácter URI legal".
tc.
1
Estoy de acuerdo con Matt aquí. Si escribe esta URL: " google.com/help/me/book name + me /? MY CRZY QUERY! + & + :)" en un navegador, codifica automáticamente los espacios pero el "&" se utiliza como valor de consulta separador y "+" se pierden.
arcot
80

Agregaré una sugerencia aquí dirigida a usuarios de Android. Puede hacer esto para evitar 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();

Puede ver que en esta URL en particular, necesito tener esos espacios codificados para poder usarlo para una solicitud.

Esto aprovecha algunas funciones disponibles 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 ningún trabajo de búsqueda / reemplazo de cadenas. En segundo lugar, este enfoque aprovecha la característica de clase URI de escapar componentes correctamente cuando construye un URI a través de componentes en lugar de hacerlo desde 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 ningún conocimiento especial de usted mismo.

Craig B
fuente
3
Buen enfoque, pero me gustaría señalar que este código no impide la doble codificación , por ejemplo,% 20 se codificó en% 2520. La respuesta de Scott no sufre de esto.
nattster
2
No puede manejarlo #.
Alston
O si solo desea hacer una cita de ruta: nuevo URI (nulo, nulo, "/ ruta con espacios", nulo, nulo) .toString ()
user1050755
1
@Stallman Si su nombre de archivo contiene #, la clase URL lo colocará en "ref" (equivalente a "fragmento" en la clase URI). Puede detectar si URL.getRef () devuelve algo que podría tratarse como parte de la ruta y pasar URL.getPath () + "#" + URL.getRef () como el parámetro "ruta" y nulo como el "fragmento" "parámetro del constructor de parámetros URI clase 7. Por defecto, la cadena después de # se trata como una referencia (o un ancla).
gouessej
49

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;
    }

}
fmucar
fuente
3
eso también requiere que rompas la url en pedazos. No hay forma de que una computadora sepa qué parte de la url codificar. Vea mi edición anterior
fmucar
44
@fmucar ¡Gracias por ese código! Cabe señalar que esto no es UTF-8. Para obtener UTF-8 simplemente preprocese la entrada con String utf8Input = new String(Charset.forName("UTF-8").encode(input).array());(tomada de aquí )
letmaik
1
Esta solución también codificará la parte "http: //" en "http% 3A% 2F% 2F", que es lo que la pregunta inicial intentó evitar.
Benjamin Piette
2
Solo pasa lo que necesita para codificar, no toda la URL. No hay forma de pasar una cadena URL completa y esperar una codificación correcta. En todos los casos, debe dividir la URL en sus piezas lógicas.
fmucar
2
Tuve problemas con esta respuesta porque no codifica caracteres inseguros para UTF-8 ... aunque puede depender de la aplicación de igual.
Tarnschaf
36

Si tiene una URL, puede pasar url.toString () a este método. Primero decodifique, para evitar la doble codificación (por ejemplo, codificar un espacio da como resultado% 20 y codificar 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 suelte 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;
    }
}
Scott Izu
fuente
1
URLDecoder.decode (cadena, "UTF-8") falla con una IllegalArgumentException cuando pasa la cadena como " google.co.in/search?q=123%!123 ". Esta es una URL válida. Supongo que esta API no funciona cuando% se usa como datos en lugar del carácter de codificación.
Medio Uno
26

Sí, la codificación de URL codificará esa cadena para que se pase correctamente en una URL a un destino final. Por ejemplo, no podría tener http://stackoverflow.com?url=http://yyy.com . La codificación Url del parámetro arreglaría ese valor del parámetro.

Entonces tengo dos opciones para ti:

  1. ¿Tiene acceso a la ruta separada del dominio? Si es así, puede simplemente codificar UrlEncode la ruta. Sin embargo, si este no es el caso, entonces la opción 2 puede ser para usted.

  2. Obtenga commons-httpclient-3.1. Esto tiene una clase URIUtil:

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

Esto generará exactamente lo que está buscando, ya que solo codificará la parte de ruta del URI.

Para su información, necesitará el códec commons y el registro de commons para que este método funcione en tiempo de ejecución.

Nathan Feger
fuente
Aparentemente, los recursos comunes apache de Sidenote dejaron de mantener URIUtil en las ramas 4.x, por lo que recomendaron usar la clase URI de JDK. Solo significa que tienes que romper la cuerda tú mismo.
Nicholi
2) Exactamente también se sugiere aquí stackoverflow.com/questions/5330104/… También utilicé la URIUtilsolución
Para Kra
11

Nitpicking: una cadena que contiene un espacio en blanco por definición no es un URI Entonces, lo que está buscando es un código que implemente el escape de URI definido en la Sección 2.1 de RFC 3986 .

Julian Reschke
fuente
Necesitamos el "cómo" en las respuestas, no "qué".
Shinzou
11

Desafortunadamente, org.apache.commons.httpclient.util.URIUtilestá en desuso y la replacement org.apache.commons.codec.net.URLCodeccodificación es adecuada para publicaciones de formularios, no en URL reales. Así que tuve que escribir mi propia función, que tiene un solo componente (no es adecuado para cadenas de consulta completas que tienen? 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);
  }
}
Jeff Tsay
fuente
Vamos, tiene que haber una biblioteca que haga esto.
Shinzou
9

URLEncoding puede codificar URL HTTP muy bien, como desafortunadamente has descubierto. La cadena que ingresó , " http://search.barnesandnoble.com/booksearch/first book.pdf", se codificó correcta y completamente en un formulario codificado en URL. Podrías pasar toda esa cadena larga de gobbledigook que obtuviste como parámetro en una URL, y podría decodificarse de nuevo exactamente en la cadena que ingresaste.

Parece que quiere hacer algo un poco diferente a pasar toda la URL como parámetro. Por lo que deduzco, está intentando crear una URL de búsqueda que se vea como " http://search.barnesandnoble.com/booksearch/whateverTheUserPassesIn ". Lo único que necesita codificar es el bit "whateverTheUserPassesIn", por lo que quizás todo lo que necesita hacer es algo como esto:

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

Eso debería producir algo más válido para usted.

Brandon Yarbrough
fuente
17
Eso reemplazaría los espacios en userInput con "+". El póster necesita reemplazarlos con "% 20".
vocaro
@vocaro: ese es un muy buen punto. URLEncoder escapa como si los argumentos fueran parámetros de consulta, no como el resto de la URL.
Brandon Yarbrough
9

Si alguien no quiere agregar una dependencia a su proyecto, estas funciones pueden ser útiles.

Pasamos la parte 'ruta' de nuestra URL aquí. Probablemente no desee pasar la URL completa como parámetro (las cadenas de consulta necesitan diferentes escapes, 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")));

}
Cuga
fuente
Gracias por esto, pero ¿qué es lo que necesito hacer para codificar un espacio -> use% 20 en su lugar según su ejemplo?
N00b Programador
Actualizado para tener en cuenta los espacios como% 20
Cuga
7

Todavía hay un problema si tienes un "/" codificado (% 2F) en tu URL.

RFC 3986 - La sección 2.2 dice: "Si los datos para un componente URI entraran en conflicto con el propósito de un carácter reservado como delimitador, entonces los datos en conflicto deben codificarse 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 - Solucionado en Apache Tomcat 6.0.10

importante: recorrido del directorio CVE-2007-0450

Tomcat permite '\', '% 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 a falso):

  • org.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH: verdadero | falso
  • org.apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH: verdadero | falso

Debido a la imposibilidad de garantizar que Tomcat maneje todas las URL como están en servidores proxy, Tomcat siempre debe estar protegido como si no se utilizara un acceso de contexto de restricción de proxy.

Afecta: 6.0.0-6.0.9

Entonces, si tiene 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 
simonox
fuente
7

Leí las respuestas anteriores para escribir mi propio método porque no podía hacer que algo funcionara correctamente usando la solución de las respuestas anteriores, me parece bien, pero si puede encontrar una URL que no funcione con esto, hágamelo 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", "%"));
}
Emilien Brigand
fuente
4

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 una muy diferente es cómo codificar los parámetros que se agregan a la URL (la parte de la consulta, detrás de "? "símbolo). Usan una codificación similar, pero no la misma.

Especialmente para la codificación del espacio en blanco. La ruta URL necesita que se codifique como% 20, mientras que la parte de la consulta permite% 20 y también el signo "+". La mejor idea es probarlo nosotros mismos contra nuestro servidor web, utilizando un navegador web.

Para ambos casos, SIEMPRE codificaría COMPONENTE POR COMPONENTE , nunca la cadena completa. De hecho, URLEncoder permite eso para la parte de consulta. Para la parte de ruta, puede usar el URI de clase, 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 usando 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.

negora
fuente
2
un código de muestra usando la URL en la pregunta sería algo bueno para poner en su respuesta
Martin Serrano
3

Quizás pueda probar UriUtils en org.springframework.web.util

UriUtils.encodeUri(input, "UTF-8")
micahli123
fuente
3

También puedes usar GUAVAy path escape: UrlEscapers.urlFragmentEscaper().escape(relativePath)

A Kra
fuente
2

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();
Martin Dimitrov
fuente
2

Tomé el contenido anterior 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 en una Cadena. Aunque, no estoy seguro de si la penalización de autoboxing vale la pena, pero si el compilador se optimiza para caracteres ASCII, entonces 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);
}
ChrisG65
fuente
1

Utilice la siguiente solución estándar de Java (pasa alrededor de 100 de los casos de prueba proporcionados por Web Plattform Tests ):

0. Pruebe si la URL ya está codificada .

1. Dividir URL en partes estructurales. Úselo java.net.URL para ello.

2. ¡ Codifique cada parte estructural correctamente!

3. Utilice IDN.toASCII(putDomainNameHere)para codificar Punycode el nombre del host!

4. Utilícelo java.net.URI.toASCIIString()para codificar en porcentaje, unicode codificado con NFC - (¡mejor sería NFKC!).

Encuentre más aquí: https://stackoverflow.com/a/49796882/1485527

jschnasse
fuente
0

He creado un nuevo proyecto para ayudar a construir URL HTTP. La biblioteca codificará automáticamente los segmentos de ruta de codificación URL y los parámetros de consulta.

Puede ver la fuente y descargar un 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

Uriah Carpenter
fuente
0

Yo tuve el mismo problema. Solucionó esto al deshacer:

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

Codifica la cadena pero omite ":" y "/".

Richard R
fuente
0

yo uso esto

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

agrega esta dependencia

 <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-text</artifactId>
        <version>1.8</version>
    </dependency>
desarrollador learn999
fuente
-2

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, galimatias la analizará correctamente .

En este caso:

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

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á java.net.URI.

Puede consultarlo en: https://github.com/smola/galimatias

smola
fuente
-3

Puedes usar una función como esta. Complete y modifíquelo según sus necesidades:

/**
     * 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", "&");
    }

Ejemplo de uso:

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

El resultado es: http://www.growup.com/folder/int%C3%A9rieur-%C3%A0_vendre?o=4

Salim Hamidi
fuente
1
Esta respuesta está incompleta sin URLCodec.
Marqués de Lorne
voto positivo para el encadenamiento .replace (), no es ideal pero es suficiente para casos de uso ad-hoc básicos
svarog
-5

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

Supongo que esto será constante y que solo el nombre de archivo cambia dinámicamente

String filename; // obtener el nombre del archivo

Cadena urlEnc = url + fileName.replace ("", "% 20");

raja
fuente
2
¿Qué pasa con todos los otros personajes ilegales?
Marqués de Lorne
-7

Qué tal si:

public String UrlEncode (String in_) {

String retVal = "";

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

return retVal;

}

MichaelICE
fuente
URLEncoder no se puede usar para escapar de caracteres URL ivalidos. Solo para codificar formas.
Archer