我的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)

我猜URLEncoder不是为编码HTTP url设计的…JavaDoc说“HTML表单编码的实用程序类”…还有别的办法吗?


当前回答

请注意,上面的大部分答案都是不正确的。

URLEncoder类,不管它的名字,不是这里需要的。不幸的是,Sun给这个类命名得如此烦人。URLEncoder用于作为参数传递数据,而不是用于对URL本身进行编码。

换句话说,“http://search.barnesandnoble.com/booksearch/first book.pdf”是URL。参数可以是,例如,“http://search.barnesandnoble.com/booksearch/first book.pdf?parameter1=this&param2=that”。参数是你使用URLEncoder的目的。

下面两个例子强调了两者之间的区别。

根据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

其他回答

uri类可以提供帮助;你可以在URL的文档中找到

注意,URI类在某些情况下确实执行组件字段的转义。建议使用URI来管理url的编码和解码

使用一个具有多个参数的构造函数,例如:

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字符的String:

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

对于像http://www.google.com/ig/api?weather=São Paulo这样的查询URL,使用构造函数的5个参数版本:

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

我把上面的内容做了一些改变。我首先喜欢正逻辑,并且我认为HashSet可能比其他选项(比如通过String进行搜索)提供更好的性能。虽然,我不确定自动装箱的代价是否值得,但如果编译器针对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);
}

请注意,上面的大部分答案都是不正确的。

URLEncoder类,不管它的名字,不是这里需要的。不幸的是,Sun给这个类命名得如此烦人。URLEncoder用于作为参数传递数据,而不是用于对URL本身进行编码。

换句话说,“http://search.barnesandnoble.com/booksearch/first book.pdf”是URL。参数可以是,例如,“http://search.barnesandnoble.com/booksearch/first book.pdf?parameter1=this&param2=that”。参数是你使用URLEncoder的目的。

下面两个例子强调了两者之间的区别。

根据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

你也可以使用GUAVA和路径逃脱器: UrlEscapers.urlFragmentEscaper () .escape (relativePath)

不幸的是,org.apache.commons.httpclient.uti.uriutil已弃用,替代的org.apache.commons.codec.net.URLCodec编码适用于表单帖子,而不适用于实际URL。所以我必须写我自己的函数,它只做一个组件(不适合有?'s和&'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);
  }
}