379 votes

Encodage de l'adresse URL HTTP en Java

Mon application Java autonome reçoit une URL (qui pointe vers un fichier) de l'utilisateur et j'ai besoin de l'atteindre et de la télécharger. Le problème auquel je suis confronté est que je ne parviens pas à encoder correctement l'adresse de l'URL HTTP...

Exemple:

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

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

me renvoie:

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

Mais, ce que je veux est

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

(espace remplacé par %20)

Je suppose que URLEncoder n'est pas conçu pour encoder les URL HTTP... La JavaDoc dit "Classe utilitaire pour l'encodage des formulaires HTML"... Y a-t-il un autre moyen de le faire?

0 votes

Pinaillage : une chaîne contenant un caractère d'espace par définition n'est pas un URI. Donc ce que vous recherchez est un code qui implémente l'échappement URI défini dans la section 2.1 du RFC 3986.

2 votes

0 votes

Le comportement est tout à fait correct. L'encodage d'URL consiste à transformer quelque chose en une chaîne qui peut être transmise en toute sécurité en tant que paramètre d'URL, et n'est absolument pas interprété en tant qu'URL. Alors que vous voulez simplement convertir une petite partie de l'URL.

313voto

Carlos Heuberger Points 11804

La classe java.net.URI peut aider; dans la documentation de l'URL que vous trouvez

Remarque, la classe URI effectue l'échappement de ses champs de composants dans certaines circonstances. La manière recommandée de gérer l'encodage et le décodage des URL est d'utiliser un URI

Utilisez l'un des constructeurs avec plus d'un argument, comme:

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

(le constructeur à un seul argument de l'URI n'échappe PAS aux caractères illégaux)


Seuls les caractères illégaux sont échappés par le code ci-dessus - il n'échappe PAS aux caractères non-ASCII (voir le commentaire de fatih).
La méthode toASCIIString peut être utilisée pour obtenir une chaîne contenant uniquement des caractères US-ASCII:

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

Pour une URL avec une requête comme http://www.google.com/ig/api?weather=São Paulo, utilisez la version du constructeur à 5 paramètres:

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

15 votes

Veuillez noter que la classe URI mentionnée ici provient de "org.apache.commons.httpclient.URI" et non de "java.net". La classe "java.net" ne gère pas les caractères illégaux dans les URI, à moins d'utiliser les constructeurs qui construisent une URL à partir de ses composants, comme mentionné dans le commentaire de Matt ci-dessous.

7 votes

@Mohamed : la classe que j'ai mentionnée et utilisée pour les tests est en réalité java.net.URI : cela a fonctionné parfaitement (Java 1.6). Je mentionnerais le nom de classe entièrement qualifié s'il ne s'agissait pas de la classe standard de Java et le lien pointe vers la documentation de java.net.URI. Et, comme le commentaire de Sudhakar, cela a résolu le problème sans inclure de "bibliothèques communes" !

1 votes

URI uri = new URI("http", "search.barnesandnoble.com", "/booksearch/é",null); Ne fait pas l'échappement correct avec cet exemple ? Cela aurait dû être échappé avec des échappements %

94voto

Matt Points 636

Veuillez noter que la plupart des réponses ci-dessus sont INCORRECTES.

La classe URLEncoder, malgré son nom, n'est PAS ce qui doit être utilisé ici. Il est malheureux que Sun ait nommé cette classe de manière aussi agaçante. URLEncoder est destiné à transmettre des données en tant que paramètres, et non pas à encoder l'URL elle-même.

En d'autres termes, "http://search.barnesandnoble.com/booksearch/first book.pdf" est l'URL. Les paramètres seraient, par exemple, "http://search.barnesandnoble.com/booksearch/first book.pdf?parameter1=this¶m2=that". Les paramètres sont ce pour quoi vous utiliseriez URLEncoder.

Les deux exemples suivants mettent en évidence les différences entre les deux.

Le suivant produit les paramètres incorrects, selon la norme HTTP. Notez que l'esperluette (&) et le signe plus (+) sont encodés de manière incorrecte.

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

Le suivant produira les paramètres corrects, avec la requête correctement encodée. Notez les espaces, les esperluettes et les signes plus.

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 votes

C'est vrai, le constructeur URI encode déjà la chaîne de requête, selon la documentation [docs.oracle.com/javase/1.4.2/docs/api/java/net/…](http://docs.oracle.com/javase/1.4.2/docs/api/java/net/URI.html#URI(java.lang.String), java.lang.String, java.lang.String, int, java.lang.String, java.lang.String, java.lang.String).

9 votes

@Draemon La réponse est correcte mais utilise la chaîne de requête de manière peu commune; un exemple plus normal pourrait être query = URLEncoder.encode(clé) + "=" + URLEncoder.encode(valeur). Les docs disent simplement que "tout caractère qui n'est pas un caractère URI légal est cité".

1 votes

Je suis d'accord avec Matt ici. Si vous tapez cette URL : "google.com/help/me/book name+me/?MY CRZY QUERY! +&+ :)" dans un navigateur, il encode automatiquement les espaces mais le "&" est utilisé comme séparateur de valeur de requête et les "+" sont perdus.

87voto

Craig B Points 2373

Je vais ajouter une suggestion ici destinée aux utilisateurs d'Android. Vous pouvez faire cela pour éviter d'avoir à utiliser des bibliothèques externes. De plus, toutes les solutions de remplacement de caractères de recherche suggérées dans certaines des réponses ci-dessus sont dangereuses et doivent être évitées.

Essayez ceci :

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

Vous pouvez voir que dans cette URL particulière, je dois encoder ces espaces pour pouvoir l'utiliser pour une requête.

Cela tire parti de quelques fonctionnalités disponibles dans les classes Android. Tout d'abord, la classe URL peut diviser une URL en ses composants appropriés, vous n'avez donc pas besoin de faire de travail de recherche/remplacement de chaîne. Deuxièmement, cette approche exploite la fonctionnalité de la classe URI qui permet d'échapper correctement aux composants lors de la construction d'une URI via des composants plutôt que à partir d'une seule chaîne.

La beauté de cette approche est que vous pouvez prendre n'importe quelle chaîne d'URL valide et la rendre fonctionnelle sans avoir besoin de connaissances spéciales à ce sujet.

4 votes

Belle approche, mais je tiens à souligner que ce code ne prévient pas le double encodage, par exemple %20 a été encodé en %2520. La réponse de Scott n'est pas affectée par cela.

0 votes

Ou si vous voulez simplement utiliser des guillemets de chemin : new URI(null, null, "/chemin avec des espaces", null, null).toString()

1 votes

@Stallman Si le nom de votre fichier contient #, la classe URL le placera dans "ref" (équivalent de "fragment" dans la classe URI). Vous pouvez détecter si URL.getRef() renvoie quelque chose qui pourrait être traité comme une partie du chemin et passer URL.getPath() + "#" + URL.getRef() en tant que paramètre "chemin" et null en tant que paramètre "fragment" du constructeur à 7 paramètres de la classe URI. Par défaut, la chaîne après # est traitée comme une référence (ou un ancre).

49voto

fmucar Points 6937

Une solution que j'ai développée et beaucoup plus stable que toute autre:

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 votes

Cela vous oblige également à diviser l'url en morceaux. Il n'y a aucun moyen pour un ordinateur de savoir quelle partie de l'URL encoder. Voir ma modification ci-dessus

4 votes

@fmucar Merci pour ce morceau de code! Il convient de noter que ce n'est pas de l'UTF-8. Pour obtenir de l'UTF-8, il suffit de prétraiter l'entrée avec String utf8Input = new String(Charset.forName("UTF-8").encode(input).array()); (issu de ici)

0 votes

En fait, je l'utilise avec un trim() et un encodage explicite maintenant, bien que ce dernier soit probablement inutile : new String(Charset.forName("UTF-8").encode(q).array(), "ISO-8859-1").trim(); Le trim() est nécessaire car encode() ajoute des octets nuls à la fin que le constructeur String ne supprime pas. Je ne sais pas si c'est entièrement correct, mais ça marche pour moi...

39voto

Scott Izu Points 299

Si vous avez une URL, vous pouvez passer url.toString() dans cette méthode. D'abord décodez, pour éviter un double encodage (par exemple, encoder un espace donne %20 et encoder un signe de pourcentage donne %25, donc un double encodage transformera un espace en %2520). Ensuite, utilisez l'URI comme expliqué ci-dessus, en ajoutant toutes les parties de l'URL (afin de ne pas perdre les paramètres de requête).

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

2 votes

URLDecoder.decode(string, "UTF-8") échoue avec une IllegalArgumentException lorsque vous transmettez la chaîne comme "google.co.in/search?q=123%!123". Ceci est une URL valide. Je suppose que cette API ne fonctionne pas lorsque % est utilisé comme donnée au lieu du caractère d'encodage.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X