Codificação de endereço de URL HTTP em Java


366

Meu aplicativo independente Java obtém uma URL (que aponta para um arquivo) do usuário e preciso acessá-lo e fazer o download. O problema que estou enfrentando é que não consigo codificar o endereço de URL HTTP corretamente ...

Exemplo:

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

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

retorna-me:

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

Mas o que eu quero é

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

(espaço substituído por% 20)

Eu acho que URLEncodernão foi projetado para codificar URLs HTTP ... O JavaDoc diz "Classe de utilitário para codificação de formulário HTML" ... Existe alguma outra maneira de fazer isso?



O comportamento é totalmente correto. A codificação de URL é transformar algo em uma sequência que pode ser transmitida com segurança como um parâmetro de URL e não é interpretada como uma URL. Considerando que você deseja apenas converter uma pequena parte da URL.
Stephen Holt

Respostas:


303

A classe java.net.URI pode ajudar; na documentação da URL que você encontra

Observe que a classe URI realiza escape de seus campos componentes em determinadas circunstâncias. A maneira recomendada de gerenciar a codificação e decodificação de URLs é usar um URI

Use um dos construtores com mais de um argumento, como:

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

(o construtor de argumento único do URI NÃO escapa caracteres ilegais)


Somente caracteres ilegais são escapados pelo código acima - NÃO escapa caracteres não-ASCII (veja o comentário de fatih).
O toASCIIStringmétodo pode ser usado para obter uma String apenas com caracteres US-ASCII:

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

Para um URL com uma consulta como http://www.google.com/ig/api?weather=São Paulo, use a versão de 5 parâmetros do construtor:

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

13
Observe que a classe URI mencionada aqui é de "org.apache.commons.httpclient.URI" e não "java.net"; o "java.net" não URI não aceita caracteres ilegais, a menos que você use construtores que constrói URL a partir de seus componentes, como a forma como mencionado no comentário Matt abaixo
Mohamed Faramawi

7
@ Mohamed: a classe que mencionei e usei para testar é java.net.URI : funcionou perfeitamente (Java 1.6). Eu mencionaria o nome completo da classe se não fosse o Java padrão e o link apontasse para a documentação de java.net.URI. E, pelo comentário de Sudhakar, ele resolveu o problema sem incluir nenhuma "biblioteca comum"!
precisa saber é o seguinte

11
URI uri = novo URI ("http", "search.barnesandnoble.com", "/ booksearch / é", nulo); Não faz o escape correto com esta amostra? Isso deveria ter sido escapado com% de escape
fmucar

@ Fatih - está correto, obrigado! Normalmente isso não deve ser um problema, mas existe uma solução simples - quase a mesma que escrevi antes. Veja a 2ª edição.
precisa saber é o seguinte

@Carlos Thx para a edição. Agora ele escapa, mas não está correto. Deve-se adicionar um% para o valor HEX do char por params Caminho intencionados E caractere deve ser convertido para% E9
fmucar

91

Esteja avisado de que a maioria das respostas acima é INCORRETA.

A URLEncoderclasse, apesar do nome, NÃO é o que precisa estar aqui. É lamentável que a Sun tenha chamado essa classe tão irritantemente. URLEncoderdestina-se a transmitir dados como parâmetros, não a codificar o próprio URL.

Em outras palavras, "http://search.barnesandnoble.com/booksearch/first book.pdf"é o URL. Os parâmetros seriam, por exemplo "http://search.barnesandnoble.com/booksearch/first book.pdf?parameter1=this&param2=that",. Os parâmetros são para o que você usaria URLEncoder.

Os dois exemplos a seguir destacam as diferenças entre os dois.

O seguinte produz os parâmetros incorretos, de acordo com o padrão HTTP. Observe que oe comercial (&) e mais (+) estão codificados incorretamente.

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:)

O seguinte produzirá os parâmetros corretos, com a consulta codificada corretamente. Observe os espaços, e comercial e marcas de mais.

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
Isso mesmo, o construtor URI já codifica a string de consulta , de acordo com a documentação 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 10/12

8
@Draemon A resposta está correta, mas usa a string de consulta de uma maneira incomum; um exemplo mais normal pode ser query = URLEncoder.encode(key) + "=" + URLEncoder.encode(value). Os documentos dizem apenas que "qualquer caractere que não seja um URI legal é citado".
tc.

11
Eu concordo com Matt aqui. Se você digitar este URL: " google.com/help/me/book name + me /? MY CRZY QUERY! + & + :)" em um navegador, ele codifica automaticamente os espaços, mas o "&" é usado como valor da consulta separador e "+" são perdidos.
precisa saber é

80

Vou adicionar uma sugestão aqui destinada aos usuários do Android. Você pode fazer isso, evitando a necessidade de obter bibliotecas externas. Além disso, todas as soluções de busca / substituição de caracteres sugeridas em algumas das respostas acima são perigosas e devem ser evitadas.

Faça uma tentativa:

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();

Você pode ver que, neste URL específico, preciso ter esses espaços codificados para que eu possa usá-lo para uma solicitação.

Isso tira proveito de alguns recursos disponíveis nas classes do Android. Primeiro, a classe de URL pode quebrar um URL em seus componentes adequados, para que você não precise executar nenhum trabalho de pesquisa / substituição de string. Em segundo lugar, essa abordagem aproveita o recurso da classe URI de componentes que escapam adequadamente quando você constrói um URI por meio de componentes, e não a partir de uma única sequência.

A vantagem dessa abordagem é que você pode pegar qualquer string de URL válida e fazê-la funcionar sem precisar de nenhum conhecimento especial dela.


3
Boa abordagem, mas gostaria de ressaltar que esse código não impede a codificação dupla , por exemplo,% 20 foi codificado em% 2520. A resposta de Scott não sofre com isso.
Nattster 03/08/19

2
Não aguenta #.
Alston

Ou se você quiser apenas fazer a citação de caminho: new URI (null, null, "/ path with spaces", null, null) .toString ()
user1050755

11
@Stallman Se o nome do seu arquivo contiver #, a classe da URL o colocará em "ref" (equivalente a "fragmento" na classe URI). Você pode detectar se URL.getRef () retorna algo que pode ser tratado como parte do caminho e passa URL.getPath () + "#" + URL.getRef () como parâmetro "path" e nulo como "fragmento" "parâmetro do construtor de parâmetros URI classe 7. Por padrão, a sequência após # é tratada como uma referência (ou uma âncora).
precisa saber é

49

uma solução que desenvolvi e muito mais estável do que qualquer outra:

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
isso também exige que você quebre o URL em pedaços. Não há como um computador saber qual parte do URL codificar. Veja minha edição acima
fmucar

4
@fmucar Obrigado por esse pedaço de código! Note-se que este não é UTF-8. Para obter UTF-8 apenas pré-processar a entrada com String utf8Input = new String(Charset.forName("UTF-8").encode(input).array());(tomadas a partir daqui )
letmaik

11
Na verdade, esta solução também codifica a parte "http: //" em "http% 3A% 2F% 2F", que é o que a pergunta inicial tentou evitar.
Benjamin Piette

2
Você passa apenas o que precisa codificar, não o URL inteiro. Não há como passar uma string de URL inteira e esperar a codificação correta. Em todos os casos, você precisa dividir o URL em suas partes lógicas.
fmucar

2
Eu tive problemas com esta resposta porque ela não codifica caracteres inseguros para UTF-8 .. embora possa ser dependente do aplicativo de mesmo nível.
Tarnschaf 9/10

36

Se você tiver um URL, poderá passar url.toString () para esse método. Primeira decodificação, para evitar a codificação dupla (por exemplo, a codificação de um espaço resulta em% 20 e a codificação de um sinal de porcentagem em% 25, ​​portanto, a codificação dupla transformará um espaço em% 2520). Em seguida, use o URI conforme explicado acima, adicionando todas as partes da URL (para não descartar os 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;
    }
}

11
URLDecoder.decode (string, "UTF-8") falha com uma IllegalArgumentException quando você passa a string como " google.co.in/search?q=123%!123 ". Este é um URL válido. Eu acho que essa API não funciona quando% é usado como dados em vez do caractere de codificação.
MediumOne 28/05

26

Sim, a codificação de URL codifica essa sequência para que ela seja passada corretamente em um URL para um destino final. Por exemplo, você não pode ter http://stackoverflow.com?url=http://yyy.com . UrlEncoding o parâmetro fixaria o valor desse parâmetro.

Então, eu tenho duas opções para você:

  1. Você tem acesso ao caminho separado do domínio? Nesse caso, você poderá simplesmente UrlEncode o caminho. No entanto, se esse não for o caso, a opção 2 pode ser para você.

  2. Obtenha o commons-httpclient-3.1. Isso tem uma classe URIUtil:

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

Isso produzirá exatamente o que você está procurando, pois codificará apenas a parte do caminho do URI.

Para sua informação, você precisará do codec comum e do log comum para esse método funcionar em tempo de execução.


Aparentemente, o sidenote apache commons parou de manter o URIUtil nos ramos 4.x, recomendando que você use a classe URI do JDK. Apenas significa que você tem que quebrar a corda sozinho.
Nicholi 23/07/2014

2) Exatamente também é sugerido aqui stackoverflow.com/questions/5330104/… Eu também usei URIUtilsolução
Para Kra

11

Nitpicking: uma string que contém um caractere de espaço em branco por definição não é um URI. Então, o que você está procurando é um código que implemente o escape de URI definido na Seção 2.1 da RFC 3986 .


Precisamos do "como" nas respostas, não do "o quê".
Shinzou

11

Infelizmente, ele org.apache.commons.httpclient.util.URIUtilestá obsoleto e a replacement org.apache.commons.codec.net.URLCodeccodificação é adequada para postagens de formulários, não em URLs reais. Então eu tive que escrever minha própria função, que faz um único componente (não é adequado para cadeias de consulta inteiras que possuem? 'E' 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);
  }
}

Vamos lá, tem que haver uma biblioteca que faça isso.
Shinzou 8/04

9

URLEncoding pode codificar URLs HTTP muito bem, como você infelizmente descobriu. A string que você inseriu, " http://search.barnesandnoble.com/booksearch/first book.pdf", foi correta e completamente codificada em um formulário codificado em URL. Você poderia transmitir toda a longa cadeia de devoradores que retornou como parâmetro em um URL, e poderia ser decodificado de volta para exatamente a cadeia pela qual você passou.

Parece que você deseja fazer algo um pouco diferente de passar todo o URL como parâmetro. Pelo que entendi, você está tentando criar um URL de pesquisa parecido com " http://search.barnesandnoble.com/booksearch/whateverTheUserPassesIn ". A única coisa que você precisa codificar é o bit "WhateverTheUserPassesIn", então talvez tudo o que você precise fazer seja algo assim:

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

Isso deve produzir algo bastante mais válido para você.


17
Isso substituiria os espaços no userInput por "+". O pôster precisa deles substituídos por "% 20".
vocaro 26/10/10

@vocaro: esse é um ponto muito bom. O URLEncoder escapa como se os argumentos fossem parâmetros de consulta, não como o restante da URL.
Brandon Yarbrough 14/02

9

Se alguém não quiser adicionar uma dependência ao seu projeto, essas funções podem ser úteis.

Passamos a parte 'path' da nossa URL para aqui. Você provavelmente não deseja passar o URL completo como parâmetro (as cadeias de consulta precisam de 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;
}

E testes:

@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")));

}

Obrigado por isso, mas o que é preciso fazer para codificar um espaço -> use% 20 como seu exemplo?
N00b Pr0grammer

Atualizado para contabilizar espaços como% 20
Cuga

7

Ainda existe um problema se você tiver um "/" (% 2F) codificado no seu URL.

A RFC 3986 - Seção 2.2 diz: "Se os dados para um componente de URI entrarem em conflito com a finalidade de um caractere reservado como delimitador, os dados conflitantes deverão ser codificados em porcentagem antes que o URI seja formado". (RFC 3986 - Seção 2.2)

Mas há um problema com o Tomcat:

http://tomcat.apache.org/security-6.html - Corrigido no Apache Tomcat 6.0.10

importante: Passagem de diretório CVE-2007-0450

O Tomcat permite '\', '% 2F' e '% 5C' [...].

As seguintes propriedades do sistema Java foram adicionadas ao Tomcat para fornecer controle adicional do manuseio dos delimitadores de caminho nas URLs (as duas opções padrão são false):

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

Devido à impossibilidade de garantir que todos os URLs sejam manipulados pelo Tomcat como nos servidores proxy, o Tomcat sempre deve ser protegido como se nenhum proxy que restringisse o acesso ao contexto fosse usado.

Afeta: 6.0.0-6.0.9

Portanto, se você tiver um URL com o caractere% 2F, o Tomcat retornará: "400 URI inválido: noSlash"

Você pode alternar a correção de bug no script de inicialização do Tomcat:

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

7

Li as respostas anteriores para escrever meu próprio método, porque não consegui que algo funcionasse corretamente usando a solução das respostas anteriores. Parece bom para mim, mas se você encontrar um URL que não funcione com isso, informe-me.

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

Eu concordo com Matt. Na verdade, nunca o vi bem explicado nos tutoriais, mas uma questão é como codificar o caminho da URL, e uma questão muito diferente é como codificar os parâmetros anexados à URL (a parte da consulta, atrás do "? "símbolo). Eles usam codificação semelhante, mas não é a mesma.

Especialmente para a codificação do caractere de espaço em branco. O caminho da URL precisa ser codificado como% 20, enquanto a parte da consulta permite% 20 e também o sinal "+". A melhor idéia é testá-lo por nós mesmos em nosso servidor da Web, usando um navegador da Web.

Nos dois casos, eu sempre codificaria COMPONENT BY COMPONENT , nunca a string inteira. Na verdade, o URLEncoder permite isso para a parte da consulta. Para a parte do caminho, você pode usar o URI da classe, embora neste caso solicite a cadeia inteira, não um único componente.

Enfim, acredito que a melhor maneira de evitar esses problemas é usar um design pessoal não conflituoso. Quão? Por exemplo, eu nunca nomearia diretórios ou parâmetros usando outros caracteres além de aZ, AZ, 0-9 e _. Dessa forma, a única necessidade é codificar o valor de cada parâmetro, pois ele pode vir de uma entrada do usuário e os caracteres usados ​​são desconhecidos.


2
código de exemplo usando o URL em questão seria uma coisa boa para colocar em sua resposta
Martin Serrano

3

Talvez possa experimentar o UriUtils em org.springframework.web.util

UriUtils.encodeUri(input, "UTF-8")

3

Você também pode usar GUAVAe localizar o caminho: UrlEscapers.urlFragmentEscaper().escape(relativePath)


2

Além da resposta de Carlos Heuberger: se for necessário um valor diferente do padrão (80), o construtor 7 param deve ser usado:

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

Peguei o conteúdo acima e mudei um pouco. Gosto primeiro da lógica positiva e achei que um HashSet poderia oferecer um desempenho melhor do que algumas outras opções, como pesquisar por uma String. Embora eu não tenha certeza se a penalidade de autoboxing vale a pena, mas se o compilador otimizar para caracteres ASCII, o custo do boxe será baixo.

/***
 * 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

Use a seguinte solução Java padrão (passa em torno de 100 dos casos de teste fornecidos pelos testes do Web Plattform ):

0. Teste se o URL já está codificado .

1. Divida o URL em partes estruturais. Use java.net.URL para isso.

2. Codifique cada parte estrutural corretamente!

3. Use IDN.toASCII(putDomainNameHere)para Punycode codificar o nome do host!

4. Use java.net.URI.toASCIIString()para codificação percentual, unicode codificado por NFC - (melhor seria NFKC!).

Encontre mais aqui: https://stackoverflow.com/a/49796882/1485527


0

Eu criei um novo projeto para ajudar a construir URLs HTTP. A biblioteca codificará automaticamente os segmentos de caminho e os parâmetros de consulta.

Você pode visualizar a fonte e baixar um binário em https://github.com/Widen/urlbuilder

O URL de exemplo nesta pergunta:

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

produz

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


0

Eu tive o mesmo problema. Resolvido isso cancelando:

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

Ele codifica a string, mas ignora ":" e "/".


0

Eu uso isso

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

adicione essa dependência

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

-2

Eu desenvolvo uma biblioteca que serve a esse propósito: galimatias . Ele analisa o URL da mesma maneira que os navegadores da web. Ou seja, se um URL funcionar em um navegador, ele será analisado corretamente por galimatias .

Nesse caso:

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

Vamos dar-lhe: http://search.barnesandnoble.com/booksearch/first%20book.pdf. Claro que esse é o caso mais simples, mas funcionará com qualquer coisa, muito além java.net.URI.

Você pode conferir em: https://github.com/smola/galimatias


-3

Você pode usar uma função como esta. Conclua e modifique-o conforme sua necessidade:

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

Exemplo de uso:

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

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


11
Esta resposta está incompleta sem o URLCodec.
Marquês de Lorne

upvote para .replace () encadeamento, não é ideal, mas é o suficiente para Ad-hoc casos básicos de uso
svarog

-5

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

Acho que isso será constante e apenas o nome do arquivo será alterado dinamicamente, portanto, obtenha o nome do arquivo

Nome do arquivo da string; // obtém o nome do arquivo

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


2
E quanto a todos os outros personagens ilegais?
Marquês de Lorne

-7

E se:

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;

}


O URLEncoder não pode ser usado para escapar de caracteres de URL ivalídeos. Apenas para codificar formulários.
Archer
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.