Encodage de L'adresse URL HTTP en Java
mon application autonome Java reçoit une URL (qui pointe vers un fichier) de l'utilisateur et j'ai besoin de l'activer et de la télécharger. Le problème auquel je suis confronté est que je ne suis pas capable de coder correctement l'adresse URL HTTP...
exemple:
URL: http://search.barnesandnoble.com/booksearch/first book.pdf
java.net.URLEncoder.encode(url.toString(), "ISO-8859-1");
me retourne:
http%3A%2F%2Fsearch.barnesandnoble.com%2Fbooksearch%2Ffirst+book.pdf
mais, ce que je veux c'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 URLs HTTP... Le JavaDoc dit "Classe D'utilité pour l'encodage de forme HTML"... Est-il un autre moyen pour ce faire?
24 réponses
the java.net.La classe URI peut vous aider; dans la documentation de L'URL vous trouverez
Note, la classe URI ne effectuer échapper de ses champs de composants dans certaines circonstances. La manière recommandée de gérer l'encodage et le décodage des URLs est D'utiliser un URI
Utiliser 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();
//or String request = uri.toString();
(la unique argument du constructeur d'URI n'échappe PAS à caractères illégaux)
seuls les caractères illégaux sont échappés par le code ci - dessus-il n'échappe pas les caractères non-ASCII (voir le commentaire de fatih).
La méthode toASCIIString
peut être utilisée pour obtenir une chaîne de caractères uniquement avec les 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 le Version à 5 paramètres du constructeur:
URI uri = new URI(
"http",
"www.google.com",
"/ig/api",
"weather=São Paulo",
null);
String request = uri.toASCIIString();
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 ici. C'est dommage que le Soleil nommé cette classe persistante. URLEncoder
est destiné à transmettre des données en tant que paramètres, et non à 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 que vous utiliseriez URLEncoder
pour.
les deux exemples suivants mettent en évidence les différences entre les deux.
ce qui suit produit les mauvais paramètres, selon le standard HTTP. Note de l'esperluette (&) et plus (+) sont codé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:)
ce qui suit produira les bons paramètres, avec la requête correctement encodée. Notez les espaces, les esperluettes, et en plus les marques.
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
je vais ajouter une suggestion ici destinée aux utilisateurs Android. Vous pouvez faire ceci qui évite d'avoir à obtenir des bibliothèques externes. En outre, toutes les solutions de recherche/remplacement de caractères suggérées dans certaines des réponses ci-dessus sont périlleuses et devraient ê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, j'ai besoin d'avoir ces espaces encodés de sorte que je puisse l'utiliser pour une requête.
Cela prend avantage de quelques fonctionnalités disponibles pour vous dans les classes Android. Tout d'abord, la classe URL peut casser une url dans ses composants appropriés de sorte qu'il n'est pas nécessaire pour vous de faire une recherche de chaîne de caractères/travail de remplacement. Deuxièmement, cette approche tire avantage de la caractéristique de la classe URI qui consiste à échapper correctement les composants lorsque vous construisez un URI à l'aide de composants plutôt qu'à 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 qu'il fonctionne sans avoir besoin d'aucune connaissance spéciale de vous-même.
une solution que j'ai développée et beaucoup plus stable que les autres:
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;
}
}
si vous avez une URL, vous pouvez passer url.toString() dans cette méthode. Premier décodage, pour éviter le double encodage (par exemple, encoder un espace donne %20 et encoder un signe de pourcentage donne %25, donc le double encodage transformera un espace en %2520). Ensuite, utilisez L'URI comme expliqué ci-dessus, en ajoutant toutes les parties de L'URL (pour que vous ne laissiez pas tomber les paramètres de la 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;
}
}
l'encodage de L'URL va encoder cette chaîne de façon à ce qu'elle soit transmise correctement dans une url vers une destination finale. Par exemple, vous ne pouvez pas avoir http://stackoverflow.com?url=http://yyy.com . Encodage le paramètre correctif de valeur de paramètre.
donc j'ai deux choix pour vous:
-
avez-vous accès à la trajectoire distincte du domaine? Si oui, vous pourriez être en mesure de simplement UrlEncode le chemin. Toutefois, si ce n'est pas le cas, alors l'option 2 peut être pour vous.
-
Obtenir commons-httpclient-3.1. Il s'agit d'un URIUtil de classe:
Système.hors.println (URIUtil.encodePath (" http://example.com/x y", "ISO-8859-1"));
ceci affichera exactement ce que vous recherchez, car il ne codera que la partie chemin de l'URI.
POUR INFO, vous aurez besoin de commons-codec et commons-logging pour que cette méthode fonctionne à l'exécution.
Nitpicking: une chaîne contenant un caractère d'espace par définition n'est pas un URI. Donc ce que vous recherchez est du code qui implémente L'URI Escape défini dans Section 2.1 de la RFC 3986 .
malheureusement, org.apache.commons.httpclient.util.URIUtil
est déprécié, et le replacement org.apache.commons.codec.net.URLCodec
ne codage approprié pour les messages de forme, pas dans les URLs réelles. Donc j'ai dû écrire ma propre fonction, qui fait un seul composant (ne convient pas pour des chaînes de requête entières qui ont ?'s et &')
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 peut très bien encoder des URLs HTTP, comme vous l'avez malheureusement découvert. La chaîne que vous avez passée, " http://search.barnesandnoble.com/booksearch/first Livre.pdf", a été correctement et complètement encodé dans une forme URL-encoded. Vous pouvez passer cette longue chaîne entière de gobbledigook que vous avez récupéré comme paramètre dans une URL, et il peut être décodé de nouveau dans exactement la chaîne que vous avez passée dans.
il semble que vous voulez faire quelque chose d'un peu différent de passer L'URL entière comme paramètre. D'après ce que j'ai compris, vous essayez de créer une URL de recherche qui ressemble à " "http://search.barnesandnoble.com/booksearch/whateverTheUserPassesIn ". La seule chose que vous devez encoder est le "quoi que theuserpassesin" bit, donc peut-être tout ce que vous devez faire est quelque chose comme ceci:
String url = "http://search.barnesandnoble.com/booksearch/" +
URLEncoder.encode(userInput,"UTF-8");
Qui devrait produire quelque chose de plus valable pour vous.
il y a toujours un problème si vous avez un " / " encodé (%2F) dans votre URL.
RFC 3986 - Section 2.2 dit: "Si les données pour le composant URI serait en conflit avec un caractère réservé du but comme un séparateur, le conflit de données doit être pour cent codé avant l'URI est formé."(RFC 3986 - Section 2.2)
mais il y a un problème avec Tomcat:
http://tomcat.apache.org/security-6.html - Fixe dans Apache Tomcat 6.0.10
important: répertoire transversal CVE-2007-0450
Tomcat permet '\', '%2F' et '%5C" [...] .
les propriétés suivantes du système Java ont été ajoutés à Tomcat pour fournir un contrôle supplémentaire de la manipulation de délimiteurs de chemin dans les URLs (les deux options par défaut à false):
- org.Apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH: vrai /faux
- org.Apache.Catalina.connecteur.CoyoteAdapter.ALLOW_BACKSLASH: vrai /faux
en Raison de l'impossibilité de garantir que toutes les URLs sont gérées par Tomcat ils sont dans des serveurs proxy, Tomcat devrait toujours être sécurisé comme si non l'accès au contexte restreint par procuration était utiliser.
affecte: 6.0.0-6.0.9
donc si vous avez une URL avec le caractère %2F, Tomcat retourne: "400 URI invalide: noSlash"
vous pouvez basculer du bugfix dans le script de démarrage Tomcat:
set JAVA_OPTS=%JAVA_OPTS% %LOGGING_CONFIG% -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true
je lis les réponses précédentes pour écrire ma propre méthode parce que je ne pouvais pas avoir quelque chose qui fonctionne correctement en utilisant la solution des réponses précédentes, il semble bon pour moi, mais si vous pouvez trouver URL qui ne fonctionne pas avec cela, s'il vous plaît laissez-moi savoir.
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", "%"));
}
si quelqu'un ne veut pas ajouter de dépendance à son projet, ces fonctions peuvent être utiles.
nous passons la partie 'chemin' de notre URL ici. Vous ne voulez probablement pas passer L'URL complète en paramètre (les chaînes de requête ont besoin d'échappées différentes, 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;
}
et essais:
@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")));
}
Je suis D'accord avec Matt. En effet, je ne l'ai jamais vu bien expliqué dans les tutoriels, mais une question Est de savoir comment encoder le chemin D'URL, et un très différent est de savoir comment encoder les paramètres qui sont annexés à L'URL (la partie requête, derrière le "?" symbole.) Ils utilisent un encodage similaire, mais pas le même.
spécialement pour l'encodage du caractère de l'espace blanc. Le chemin D'URL doit être encodé en %20, alors que la partie requête permet %20 et aussi le signe"+". Le la meilleure idée est de le tester par nous-mêmes contre notre serveur Web, en utilisant un navigateur web.
pour les deux cas, I toujours coderait composant par composant , jamais la chaîne entière. En effet URLEncoder permet cela pour la partie requête. Pour la partie chemin, vous pouvez utiliser la classe URI, bien que dans ce cas elle demande la chaîne entière, pas un seul composant.
quoi qu'il en soit, je crois que la meilleure façon de éviter ces problèmes est d'utiliser une conception personnelle non conflictuelle. comment? Par exemple, Je ne nommerai jamais des répertoires ou des paramètres en utilisant d'autres caractères que A-Z, A-Z, 0-9 et _ . De cette façon, le seul besoin est d'encoder la valeur de chaque paramètre, car il peut provenir d'une entrée de l'utilisateur et les caractères utilisés sont inconnus.
Peut-être pouvez essayer UriUtils dans org.springframework.Web.jusqu'à 151940920"
UriUtils.encodeUri(input, "UTF-8")
vous pouvez également utiliser GUAVA
et escaper chemin:
UrlEscapers.urlFragmentEscaper().escape(relativePath)
en plus de la réponse de Carlos Heuberger: si un autre que le défaut (80) est nécessaire, le constructeur 7 param doit être utilisé:
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();
j'ai créé un nouveau projet pour aider à construire des URLs HTTP. La bibliothèque encodera automatiquement les segments de chemin D'URL et les paramètres de requête.
vous pouvez voir la source et télécharger un binaire à https://github.com/Widen/urlbuilder
L'exemple D'URL dans cette question:
new UrlBuilder("search.barnesandnoble.com", "booksearch/first book.pdf").toString()
produit
http://search.barnesandnoble.com/booksearch/first%20book.pdf
j'ai eu le même problème.
android.net.Uri.encode(urlString, ":/");
Il encode la chaîne mais saute ":" et "/".
1. divise la chaîne D'URL en parties structurelles. Utilisez java.net.URL
pour cela.
2. encodez chaque pièce de structure correctement!
3. utiliser IDN.toASCII(putDomainNameHere)
à Punycode encoder le nom d'hôte!
4. Utiliser java.net.URI.toASCIIString()
% -encoder, NFC codé en unicode (mieux serait NFKC!). Pour plus d'info voir: comment coder correctement cette URL
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);
Imprime
http://search.barnesandnoble.com/booksearch/first%20book.pdf
j'ai pris le contenu ci-dessus et l'ai changé un peu. J'aime la logique positive d'abord, et j'ai pensé qu'un HashSet pourrait donner de meilleures performances que d'autres options, comme la recherche à travers une chaîne. Bien que, je ne suis pas sûr que la pénalité de l'autoboxing en vaille la peine, mais si le compilateur optimise pour ASCII chars, alors le coût de la boxe sera faible.
/***
* 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);
}
je développe une bibliothèque qui sert ce but: galimatias . Il analyse L'URL de la même façon que les navigateurs web. C'est-à-dire que si une URL fonctionne dans un navigateur, elle sera correctement analysée par galimatias .
dans ce cas:
// Parse
io.mola.galimatias.URL.parse(
"http://search.barnesandnoble.com/booksearch/first book.pdf"
).toString()
vous donnera: http://search.barnesandnoble.com/booksearch/first%20book.pdf
. Bien sûr, c'est le cas le plus simple, mais ça marchera avec n'importe quoi, bien au-delà de java.net.URI
.
Vous pouvez le vérifier sortie: https://github.com/smola/galimatias
Vous pouvez utiliser une fonction comme celle-ci. Remplir et de le modifier à votre besoin :
/**
* 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", "&");
}
exemple d'utilisation:
String urlToEncode = ""http://www.growup.com/folder/intérieur-à_vendre?o=4";
Utils.encodeUrl (urlToEncode , "UTF-8")
le résultat est: http://www.growup.com/folder/int%C3%A9rieur-%C3%A0_vendre?o=4
String url= " http://search.barnesandnoble.com/booksearch / ;
ce sera constant je suppose et seulement le nom de fichier change dyamiquement donc obtenir le nom de fichier
String filename; // obtenir le nom du fichier
Chaîne urlEnc=adresse+nom de fichier.remplacer(" ","%20");
Que Diriez-vous de:
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;
}