383

私の Java スタンドアロン アプリケーションは、ユーザーから URL (ファイルを指す) を取得し、それをクリックしてダウンロードする必要があります。私が直面している問題は、HTTP URL アドレスを適切にエンコードできないことです...

例:

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

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

私を返します:

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

でも、欲しいのは

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

(スペースは %20 に置き換えられます)

HTTP URLをエンコードするように設計されていないと思いURLEncoderます... JavaDocには「HTMLフォームエンコーディング用のユーティリティクラス」と書かれています...これを行う他の方法はありますか?

4

24 に答える 24

316

java.net.URIクラスが役に立ちます。あなたが見つけたURLのドキュメントで

URI クラスは、特定の状況でコンポーネント フィールドのエスケープを実行することに注意してください。URL のエンコードとデコードを管理するための推奨される方法は、URI を使用することです。

次のように、複数の引数を持つコンストラクターの 1 つを使用します。

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

(URI の単一引数コンストラクターは不正な文字をエスケープしません)


上記のコードでは、不正な文字のみがエスケープされます。非 ASCII 文字はエスケープされません (fatih のコメントを参照)。
このtoASCIIStringメソッドを使用して、US-ASCII 文字のみを含む文字列を取得できます。

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

のようなクエリを含む URL の場合http://www.google.com/ig/api?weather=São Paulo、コンストラクタの 5 パラメータ バージョンを使用します。

URI uri = new URI(
        "http", 
        "www.google.com", 
        "/ig/api",
        "weather=São Paulo",
        null);
String request = uri.toASCIIString();
于 2009-04-07T09:12:13.867 に答える
95

上記の回答のほとんどが間違っていることに注意してください。

クラスは、名前にもかかわらず、URLEncoderここにある必要があるものではありません。残念なことに、Sun はこのクラスに非常に迷惑な名前を付けました。 URLEncoderURL自体をエンコードするためではなく、データをパラメーターとして渡すためのものです。

つまり、"http://search.barnesandnoble.com/booksearch/first book.pdf"URLです。パラメータは、たとえば"http://search.barnesandnoble.com/booksearch/first book.pdf?parameter1=this&param2=that". パラメータは、使用するものですURLEncoder

次の 2 つの例は、両者の違いを強調しています。

以下は、HTTP 標準に従って、間違ったパラメーターを生成します。アンパサンド (&) とプラス (+) が正しくエンコードされていないことに注意してください。

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

以下は、適切にエンコードされたクエリを使用して、正しいパラメーターを生成します。スペース、アンパサンド、およびプラス記号に注意してください。

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
于 2010-04-07T21:01:40.700 に答える
88

ここで、Android ユーザー向けの提案を 1 つ追加します。これにより、外部ライブラリを取得する必要がなくなります。また、上記の回答の一部で提案されているすべての検索/置換文字ソリューションは危険であり、避ける必要があります。

これを試してください:

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

この特定の URL では、リクエストに使用できるようにこれらのスペースをエンコードする必要があることがわかります。

これは、Android クラスで利用できるいくつかの機能を利用します。まず、URL クラスは URL を適切なコンポーネントに分割できるため、文字列の検索/置換作業を行う必要はありません。第 2 に、このアプローチは、単一の文字列からではなくコンポーネントを介して URI を構築するときに、コンポーネントを適切にエスケープする URI クラスの機能を利用します。

このアプローチの優れた点は、有効な URL 文字列を取得して、特別な知識を必要とせずに機能させることができることです。

于 2012-01-22T17:04:01.840 に答える
49

私が開発したソリューションで、他のソリューションよりもはるかに安定しています。

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

}
于 2011-01-05T15:28:36.307 に答える
39

URL がある場合は、このメソッドに url.toString() を渡すことができます。最初にデコードして、二重エンコードを回避します (たとえば、スペースをエンコードすると %20 になり、パーセント記号をエンコードすると %25 になるため、二重エンコードするとスペースが %2520 に変わります)。次に、上で説明したように URI を使用して、URL のすべての部分を追加します (クエリ パラメータを削除しないようにします)。

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;
    }
}
于 2012-03-03T02:12:46.413 に答える
27

ええ、URLエンコーディングはその文字列をエンコードして、最終的な宛先にURLで適切に渡されるようにします. たとえば、http://stackoverflow.com?url=http://yyy.comを持つことはできません。パラメータを UrlEncoding すると、そのパラメータ値が修正されます。

だから私はあなたのために2つの選択肢があります:

  1. ドメインとは別のパスにアクセスできますか? その場合は、パスを単純に UrlEncode できる場合があります。ただし、そうでない場合は、オプション 2 が適している可能性があります。

  2. commons-httpclient-3.1 を入手します。これにはクラス URIUtil があります。

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

これは、URI のパス部分のみをエンコードするため、探しているものを正確に出力します。

参考までに、このメソッドを実行時に機能させるには、commons-codec と commons-logging が必要です。

于 2009-04-07T03:34:45.953 に答える
11

残念ながら、org.apache.commons.httpclient.util.URIUtilは非推奨であり、replacement org.apache.commons.codec.net.URLCodec実際のURLではなく、フォームの投稿に適したコーディングを行っています。そのため、単一のコンポーネントを実行する独自の関数を作成する必要がありました(?と&を含むクエリ文字列全体には適していません)。

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);
  }
}
于 2011-06-30T06:29:43.323 に答える
8

以前の回答の解決策を使用して何かを適切に機能させることができなかったため、独自の方法を作成するために以前の回答を読みました。

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", "%"));
}
于 2015-06-04T10:02:20.380 に答える
7

URL にエンコードされた「/」(%2F) がある場合は、まだ問題があります。

RFC 3986 - セクション 2.2 は次のように述べています。(RFC 3986 - セクション 2.2)

しかし、Tomcat には問題があります。

http://tomcat.apache.org/security-6.html - Apache Tomcat 6.0.10 で修正済み

重要: ディレクトリ トラバーサル CVE-2007-0450

Tomcat は '\'、'%2F' および '%5C' [...] を許可します。

次の Java システム プロパティが Tomcat に追加され、URL のパス区切り文字の処理をさらに制御できるようになりました (両方のオプションのデフォルトは false)。

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

すべての URL がプロキシ サーバーと同じように Tomcat によって処理されることを保証することは不可能であるため、Tomcat は常に、コンテキスト アクセスを制限するプロキシが使用されていないかのように保護する必要があります。

影響: 6.0.0-6.0.9

したがって、%2F 文字を含む URL を取得した場合、Tomcat は「400 無効な URI: noSlash」を返します。

Tomcat 起動スクリプトでバグ修正を切り替えることができます。

set JAVA_OPTS=%JAVA_OPTS% %LOGGING_CONFIG%   -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true 
于 2010-09-28T07:33:51.397 に答える
4

GUAVAパス エスケーパー を使用することもできます。UrlEscapers.urlFragmentEscaper().escape(relativePath)

于 2016-05-18T11:54:16.020 に答える
4

私はマットに同意します。実際、チュートリアルで十分に説明されているのを見たことがありませんが、1 つの問題は URL パスをエンコードする方法であり、非常に異なる問題は、URL に追加されるパラメーターをエンコードする方法です ("? "記号)。それらは同様のエンコーディングを使用しますが、同じではありません。

特に空白文字のエンコーディング用。URL パスは %20 としてエンコードする必要がありますが、クエリ部分では %20 と "+" 記号を使用できます。最善の方法は、Web ブラウザーを使用して、Web サーバーに対して自分でテストすることです。

どちらの場合も、文字列全体ではなく、常に COMPONENT BY COMPONENTエンコードします。実際、URLEncoder はクエリ部分でそれを可能にします。パス部分にはクラス URI を使用できますが、この場合は単一のコンポーネントではなく、文字列全体を要求します。

とにかく、これらの問題を回避する最善の方法は、個人的な非競合的なデザインを使用することだと思います. どのように?たとえば、Z、AZ、0-9、および _ 以外の文字を使用して、ディレクトリやパラメーターに名前を付けることは決してありません。そうすれば、すべてのパラメーターの値をエンコードするだけで済みます。これは、ユーザー入力に由来する可能性があり、使用される文字が不明であるためです。

于 2011-06-04T14:03:06.277 に答える
2

Carlos Heuberger の回答に加えて、デフォルト (80) とは異なるものが必要な場合は、7 パラメータのコンストラクタを使用する必要があります。

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();
于 2011-07-29T13:20:53.443 に答える
2

上記の内容を少し変えてみました。私は最初に正論理が好きで、文字列の検索など、HashSet が他のいくつかのオプションよりも優れたパフォーマンスを提供する可能性があると考えました。ただし、オートボクシングのペナルティがそれだけの価値があるかどうかはわかりませんが、コンパイラが ASCII 文字を最適化する場合、ボクシングのコストは低くなります。

/***
 * 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);
}
于 2018-08-08T19:41:51.027 に答える
0

HTTPURLの構築に役立つ新しいプロジェクトを作成しました。ライブラリは、パスセグメントとクエリパラメータを自動的にURLエンコードします。

ソースを表示し、 https://github.com/Widen/urlbuilderでバイナリをダウンロードできます

この質問のURLの例:

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

を生成します

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

于 2011-01-15T05:00:00.093 に答える
0

私も同じ問題を抱えていました。unsing でこれを解決しました:

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

文字列をエンコードしますが、「:」と「/」はスキップします。

于 2017-04-03T09:55:32.027 に答える
-1

私はこの目的に役立つライブラリを開発しています: galimatias . Web ブラウザーと同じ方法で URL を解析します。つまり、URL がブラウザーで機能する場合、それはgalimatiasによって正しく解析されます。

この場合:

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

あなたに与えます: http://search.barnesandnoble.com/booksearch/first%20book.pdf. もちろん、これは最も単純なケースですが、java.net.URI.

https://github.com/smola/galimatiasで確認できます。

于 2014-03-18T14:57:01.053 に答える
-3

こんな機能が使えます。必要に応じてそれを完成させて修正してください:

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

使用例:

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

結果は: http://www.growup.com/folder/int%C3%A9rieur-%C3%A0_vendre?o=4

于 2014-08-22T23:13:12.767 に答える
-7

どうですか:

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;

}

于 2012-03-20T01:11:54.883 に答える