webentwicklung-frage-antwort-db.com.de

HTTP URL Address Encoding in Java

Meine Java Standalone-Anwendung erhält eine URL (die auf eine Datei verweist) vom Benutzer, und ich muss sie aufrufen und herunterladen. Das Problem besteht darin, dass ich die nicht codieren kann HTTP URL Adresse richtig ...

Beispiel:

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

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

gibt mich zurück:

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

Aber was ich will, ist

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

(Leerzeichen ersetzt durch% 20)

Ich vermute, URLEncoder ist nicht für die Codierung von HTTP-URLs ausgelegt ... In JavaDoc steht "Utility-Klasse für die Codierung von HTML-Formularen" ... Gibt es eine andere Möglichkeit, dies zu tun?

354
Sudhakar R

Die Java.net.URI Klasse kann helfen; in der Dokumentation der URL finden Sie

Beachten Sie, dass die URI-Klasse unter bestimmten Umständen einen Escape-Vorgang für ihre Komponentenfelder ausführt. Die empfohlene Methode zum Verwalten der Codierung und Decodierung von URLs ist die Verwendung eines URI

Verwenden Sie einen der Konstruktoren mit mehr als einem Argument wie:

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

(der Konstruktor mit einem Argument der URI entgeht KEINEN unzulässigen Zeichen)


Nur unzulässige Zeichen werden mit dem obigen Code maskiert. Nicht-ASCII-Zeichen werden NICHT maskiert (siehe Kommentar von fatih).
Mit der toASCIIString -Methode kann ein String nur mit US-ASCII-Zeichen abgerufen werden:

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

Für eine URL mit einer Abfrage wie http://www.google.com/ig/api?weather=São Paulo, verwende die 5-Parameter-Version des Konstruktors:

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

Bitte seien Sie gewarnt, dass die meisten der obigen Antworten nicht korrekt sind.

Die Klasse URLEncoder muss trotz ihres Namens NICHT hier sein. Es ist bedauerlich, dass Sun diese Klasse so ärgerlich benannt hat. URLEncoder dient zum Übergeben von Daten als Parameter, nicht zum Codieren der URL selbst.

Mit anderen Worten, "http://search.barnesandnoble.com/booksearch/first book.pdf" ist die URL. Parameter wären zum Beispiel "http://search.barnesandnoble.com/booksearch/first book.pdf?parameter1=this&param2=that". Für diese Parameter würden Sie URLEncoder verwenden.

Die folgenden beiden Beispiele verdeutlichen die Unterschiede zwischen den beiden.

Das Folgende erzeugt die falschen Parameter gemäß dem HTTP-Standard. Beachten Sie, dass das kaufmännische Und (&) und das Pluszeichen (+) falsch codiert sind.

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

Das Folgende erzeugt die richtigen Parameter, wobei die Abfrage richtig codiert ist. Beachten Sie die Leerzeichen, Et-Zeichen und Pluszeichen.

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
85
Matt

Ich werde hier einen Vorschlag für Android Benutzer hinzufügen. Sie können dies tun, um externe Bibliotheken zu vermeiden. Außerdem werden alle in einigen der vorgeschlagenen Such-/Ersetzungslösungen für Zeichen verwendet Die obigen Antworten sind gefährlich und sollten vermieden werden.

Probieren Sie es aus:

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

Sie können sehen, dass in dieser bestimmten URL diese Leerzeichen codiert sein müssen, damit ich sie für eine Anfrage verwenden kann.

Hierbei werden einige Funktionen ausgenutzt, die Ihnen in Android Klassen zur Verfügung stehen. Erstens kann die URL-Klasse eine URL in die richtigen Komponenten aufteilen, sodass Sie keine Zeichenfolgen suchen/ersetzen müssen Zweitens nutzt dieser Ansatz die URI-Klassenfunktion, Komponenten ordnungsgemäß zu maskieren, wenn Sie einen URI nicht aus einer einzelnen Zeichenfolge, sondern über Komponenten erstellen.

Das Schöne an diesem Ansatz ist, dass Sie jede gültige URL-Zeichenfolge verwenden können, ohne dass Sie spezielle Kenntnisse benötigen.

76
Craig B

eine Lösung, die ich entwickelt habe und die viel stabiler ist als jede andere:

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 " %$&+,/:;[email protected]<>#%".indexOf(ch) >= 0;
    }

}
48
fmucar

Wenn Sie eine URL haben, können Sie url.toString () an diese Methode übergeben. Erstes Dekodieren, um Doppelkodierung zu vermeiden (z. B. führt das Kodieren eines Leerzeichens zu% 20 und das Kodieren eines Prozentzeichens zu% 25, ​​sodass durch Doppelkodierung ein Leerzeichen in% 2520 umgewandelt wird). Verwenden Sie dann den URI wie oben erläutert, und fügen Sie alle Teile der URL hinzu (damit Sie die Abfrageparameter nicht löschen).

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;
    }
}
35
Scott Izu

Ja, die URL-Codierung wird diese Zeichenfolge codieren, damit sie ordnungsgemäß in einer URL an ein endgültiges Ziel übergeben wird. Zum Beispiel könnten Sie nicht haben http://stackoverflow.com?url=http://yyy.com . Durch UrlEncoding des Parameters wird dieser Parameterwert festgelegt.

Also habe ich zwei Möglichkeiten für dich:

  1. Haben Sie Zugriff auf den Pfad, der von der Domain getrennt ist? In diesem Fall können Sie den Pfad möglicherweise einfach mit UrlEncode versehen. Ist dies jedoch nicht der Fall, ist Option 2 möglicherweise für Sie geeignet.

  2. Holen Sie sich commons-httpclient-3.1. Dies hat eine Klasse URIUtil:

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

Dies gibt genau das aus, wonach Sie suchen, da es nur den Pfad-Teil der URI codiert.

Zu Ihrer Information, Sie benötigen Commons-Codec und Commons-Logging, damit diese Methode zur Laufzeit funktioniert.

26
Nathan Feger

Nitpicking: Ein String, der per Definition ein Leerzeichen enthält, ist kein URI. Was Sie also suchen, ist Code, der die in Abschnitt 2.1 von RFC 3986 definierte URI-Escapierung implementiert.

11
Julian Reschke

Unglücklicherweise, org.Apache.commons.httpclient.util.URIUtil ist veraltet und der replacement org.Apache.commons.codec.net.URLCodec codiert nicht in tatsächlichen URLs, sondern in Formularposts. Ich musste also meine eigene Funktion schreiben, die eine einzelne Komponente ausführt (nicht geeignet für ganze Abfragezeichenfolgen mit? Und &).

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);
  }
}
11
Jeff Tsay

Wenn niemand eine Abhängigkeit zu seinem Projekt hinzufügen möchte, können diese Funktionen hilfreich sein.

Wir geben den 'Pfad'-Teil unserer URL hier ein. Wahrscheinlich möchten Sie nicht die vollständige URL als Parameter übergeben (Abfragezeichenfolgen benötigen unterschiedliche Escapezeichen usw.).

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

Und Tests:

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

}
8
Cuga

Es besteht weiterhin ein Problem, wenn Ihre URL ein "/" (% 2F) enthält.

RFC 3986 - In Abschnitt 2.2 heißt es: "Wenn Daten für eine URI-Komponente mit dem Zweck eines reservierten Zeichens als Begrenzer in Konflikt stehen, müssen die in Konflikt stehenden Daten vor der Bildung des URI in Prozent codiert werden." (RFC 3986 - Abschnitt 2.2)

Es gibt jedoch ein Problem mit Tomcat:

http://Tomcat.Apache.org/security-6.html - In Apache Tomcat 6.0.10 behoben

wichtig: Directory Traversal CVE-2007-0450

Tomcat erlaubt '\', '% 2F' und '% 5C' [...].

Die folgenden Java - Systemeigenschaften wurden zu Tomcat hinzugefügt, um die Behandlung von Pfadtrennzeichen in URLs zusätzlich zu steuern (beide Optionen sind standardmäßig auf false eingestellt):

  • org.Apache.Tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH: true | false
  • org.Apache.catalina.connector.CoyoteAdapter.ALLOW_BACKSLASH: true | false

Da nicht garantiert werden kann, dass alle URLs von Tomcat wie auf Proxyservern behandelt werden, sollte Tomcat immer so gesichert werden, als ob kein Proxy verwendet würde, der den Kontextzugriff einschränkt.

Auswirkungen: 6.0.0-6.0.9

Wenn Sie also eine URL mit dem Zeichen% 2F haben, gibt Tomcat Folgendes zurück: "400 Invalid URI: noSlash"

Sie können den Bugfix im Tomcat-Startskript abschalten:

set Java_OPTS=%Java_OPTS% %LOGGING_CONFIG%   -Dorg.Apache.Tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true 
7
simonox

Wie Sie leider festgestellt haben, kann URLEncoding problemlos HTTP-URLs codieren. Die von Ihnen übergebene Zeichenfolge " http://search.barnesandnoble.com/booksearch/first book.pdf" wurde korrekt und vollständig in eine URL-codierte Form umgewandelt. Sie könnten die gesamte lange Zeichenfolge von Gobbledigook übergeben, die Sie als Parameter in einer URL zurückerhalten haben, und sie könnte in genau die Zeichenfolge dekodiert werden, die Sie übergeben haben.

Es hört sich so an, als ob Sie etwas anderes tun möchten, als die gesamte URL als Parameter zu übergeben. Nach meinen Erkenntnissen versuchen Sie, eine Such-URL zu erstellen, die wie folgt aussieht: " http://search.barnesandnoble.com/booksearch/whateverTheUserPassesIn ". Das einzige, was Sie codieren müssen, ist das "whateverTheUserPassesIn" -Bit. Vielleicht müssen Sie also nur Folgendes tun:

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

Das sollte etwas für Sie Gültigeres ergeben.

7

Ich habe die vorherigen Antworten gelesen, um meine eigene Methode zu schreiben, da mit der Lösung der vorherigen Antworten etwas nicht richtig funktioniert. Es sieht gut für mich aus. Wenn Sie jedoch eine URL finden, die damit nicht funktioniert, lassen Sie es mich bitte wissen.

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", "%"));
}
7
Emilien Brigand

Ich stimme Matt zu. Ich habe es in Tutorials noch nie gut erklärt gesehen, aber eine Sache ist, wie man den URL-Pfad codiert, und eine ganz andere, wie man die Parameter codiert, die an die URL angehängt werden (der Abfrageteil, hinter dem "?" "Symbol). Sie verwenden eine ähnliche Codierung, aber nicht die gleiche.

Speziell für die Kodierung des Leerzeichens. Der URL-Pfad muss als% 20 codiert sein, während der Abfrageteil% 20 und auch das "+" - Zeichen zulässt. Die beste Idee ist, es selbst mit einem Webbrowser auf unserem Webserver zu testen.

In beiden Fällen würde I IMMER KOMPONENTE FÜR KOMPONENTE codieren, niemals die gesamte Zeichenfolge. In der Tat erlaubt URLEncoder dies für den Abfrageteil. Für den Pfadteil können Sie den Klassen-URI verwenden, obwohl in diesem Fall der gesamte String und nicht eine einzelne Komponente abgefragt wird.

Wie auch immer, ich glaube, dass der beste Weg, um diese Probleme zu vermeiden, ist, ein persönliches, nicht konfliktbehaftetes Design zu verwenden. Wie? Zum Beispiel würde ich niemals Verzeichnisse oder Parameter mit anderen Zeichen als a-Z, A-Z, 0-9 und _ benennen. Auf diese Weise muss nur der Wert jedes Parameters codiert werden, da er möglicherweise von einer Benutzereingabe stammt und die verwendeten Zeichen unbekannt sind.

4
negora

Sie können auch GUAVA und Pfad-Escaper verwenden: UrlEscapers.urlFragmentEscaper().escape(relativePath)

3
To Kra

Vielleicht kann man versuchen riUtils in org.springframework.web.util

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

Zusätzlich zur Antwort von Carlos Heuberger: Wenn ein anderer als der Standardwert (80) benötigt wird, sollte der 7-Parameter-Konstruktor verwendet werden:

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
Martin Dimitrov

Ich habe den obigen Inhalt genommen und ein wenig geändert. Ich mag zuerst positive Logik, und ich dachte, ein HashSet bietet möglicherweise eine bessere Leistung als einige andere Optionen, z. B. das Durchsuchen eines Strings. Ich bin mir zwar nicht sicher, ob sich die Autoboxing-Strafe lohnt, aber wenn der Compiler für ASCII) Zeichen optimiert, sind die Kosten für das Boxen niedrig.

/***
 * 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);
}
2
ChrisG65

Verwenden Sie die folgende Standardlösung Java) (übergibt ungefähr 100 der Testfälle, die von Web Plattform Tests bereitgestellt werden):

0. Prüfen Sie, ob die URL bereits verschlüsselt ist .

1. URL in Strukturteile aufteilen. Verwenden Sie dazu Java.net.URL.

2. Codieren Sie jedes Bauteil richtig!

3. Verwenden Sie IDN.toASCII(putDomainNameHere), um Punycode den Hostnamen zu codieren!

4. Verwenden Sie Java.net.URI.toASCIIString() zum prozentualen Codieren, NFC codierten Unicode besser wäre NFKC!).

Weitere Informationen finden Sie hier: --- (https://stackoverflow.com/a/49796882/1485527

1
jschnasse

Ich hatte das gleiche problem Gelöst durch:

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

Es kodiert den String, überspringt aber ":" und "/".

0
Richard R

Ich habe ein neues Projekt erstellt, um HTTP-URLs zu erstellen. Die Bibliothek codiert automatisch Pfadsegmente und Abfrageparameter per URL.

Sie können den Quellcode anzeigen und eine Binärdatei unter https://github.com/Widen/urlbuilder herunterladen

Die Beispiel-URL in dieser Frage:

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

produziert

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

0
Uriah Carpenter