Java中的HTTP URL地址编码

Sud*_*r R 356 java http urlencode

我的Java独立应用程序从用户获取一个URL(指向一个文件),我需要点击它并下载它.我面临的问题是我无法正确编码HTTP URL地址......

例:

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

java.net.URLEncoder.encode(url.toString(), "ISO-8859-1");
Run Code Online (Sandbox Code Playgroud)

回报我:

http%3A%2F%2Fsearch.barnesandnoble.com%2Fbooksearch%2Ffirst+book.pdf
Run Code Online (Sandbox Code Playgroud)

但是,我想要的是

http://search.barnesandnoble.com/booksearch/first%20book.pdf
Run Code Online (Sandbox Code Playgroud)

(空间由%20取代)

我猜URLEncoder不是为了编码HTTP URL而设计的... JavaDoc说"用于HTML表单编码的实用程序类"......有没有其他方法可以做到这一点?

use*_*421 295

java.net.URI中的类可以帮助; 在您找到的URL文档中

注意,URI类确实在某些情况下执行其组件字段的转义.管理URL编码和解码的推荐方法是使用URI

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

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

(URI的单参数构造函数不会转义非法字符)


只有非法字符才会被上面的代码转义 - 它不会转义非ASCII字符(请参阅fatih的评论).
toASCIIString方法可用于仅使用US-ASCII字符获取String:

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

对于带有查询的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();
Run Code Online (Sandbox Code Playgroud)

  • 请注意,这里提到的URI类来自"org.apache.commons.httpclient.URI"而不是"java.net","java.net"没有URI不接受非法字符,除非你会使用从其组件构建URL的构造函数,如下面的Matt注释中提到的方式 (11认同)
  • @Mohamed:我提到并用于测试**的类实际上是**`java.net.URI`:它工作得很好(Java 1.6).如果不是标准的Java类,我会提到完全限定的类名,链接指向`java.net.URI`的文档.并且,通过Sudhakar的评论,它解决了问题,而不包括任何"公共图书馆"! (7认同)
  • URI uri = new URI("http", "search.barnesandnoble.com", "/booksearch/é",null); 没有对这个样本进行正确的转义吗?这应该用 % 转义来转义 (2认同)
  • 不要忘记最后的空,就像我做的那样......哈哈 - (2认同)

小智 86

请注意,上面的大多数答案都是不正确的.

这个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:)
Run Code Online (Sandbox Code Playgroud)

以下将生成正确的参数,并对查询进行正确编码.请注意空格,&符号和加号.

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
Run Code Online (Sandbox Code Playgroud)

  • 这是错误的 - 您对查询部分进行了双重编码. (31认同)
  • @Draemon答案是正确的,但以不常见的方式使用查询字符串; 一个更正常的例子可能是`query = URLEncoder.encode(key)+"="+ URLEncoder.encode(value)`.文档只是说"引用任何不合法URI字符的字符". (7认同)
  • 没错,根据文档http://docs.oracle.com/javase/1.4.2/docs/api/java/net/URI.html#URI(java.lang.String),URI构造函数已经编码了查询字符串. ,java.lang.String,java.lang.String,int,java.lang.String,java.lang.String,java.lang.String) (2认同)

Cra*_*g B 76

我将在这里针对Android用户添加一个建议.您可以这样做,避免必须获得任何外部库.此外,在上面的一些答案中建议的所有搜索/替换字符解决方案都是危险的,应该避免.

尝试一下:

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();
Run Code Online (Sandbox Code Playgroud)

你可以看到,在这个特定的URL中,我需要对这些空间进行编码,以便我可以将它用于请求.

这利用了Android类中可用的一些功能.首先,URL类可以将url分解为其正确的组件,因此您无需进行任何字符串搜索/替换工作.其次,当您通过组件而不是单个字符串构造URI时,此方法利用了正确转义组件的URI类功能.

这种方法的优点在于,您可以使用任何有效的URL字符串并使其工作,而无需您自己了解任何特殊知识.

  • 很好的方法,但我想指出这个代码不会阻止_double encoding_,例如%20被编码到%2520.[斯科特的答案](http://stackoverflow.com/a/9542781/554894)不会受此影响. (3认同)
  • 它无法处理`#`. (2认同)

fmu*_*car 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;
    }

}
Run Code Online (Sandbox Code Playgroud)

  • @fmucar感谢您的代码!应该注意的是,这不是UTF-8.要获得UTF-8,只需使用`String utf8Input = new String(Charset.forName("UTF-8").encode(input).array());`(取自[here](http: //stackoverflow.com/questions/5729806/encode-string-to-utf-8/5729828#5729828)) (4认同)
  • 这也要求你把网址分成几块.计算机无法知道要编码的URL的哪个部分.看我上面的编辑 (3认同)
  • 该解决方案实际上还将“http://”部分编码为“http%3A%2F%2F”,这是最初的问题试图避免的。 (2认同)
  • 您只传递编码所需的内容,而不是整个URL.无法传递整个URL字符串并期望正确的编码.在所有情况下,您需要将URL分解为逻辑部分. (2认同)
  • 我有这个答案的问题,因为它不会将不安全的字符编码为UTF-8 ..但可能依赖于对等应用程序. (2认同)

Sco*_*Izu 35

如果您有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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 当您将字符串作为“https://www.google.co.in/search?q=123%!123”传递时,URLDecoder.decode(string, "UTF-8") 失败并显示 IllegalArgumentException。这是一个有效的 URL。我猜当 % 用作数据而不是编码字符时,这个 API 不起作用。 (2认同)

Nat*_*ger 26

是的URL编码将编码该字符串,以便它可以在URL中正确传递到最终目的地.例如,您无法访问http://stackoverflow.com?url=http://yyy.com.UrlEncoding参数将修复该参数值.

所以我有两个选择:

  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来使此方法在运行时工作.


Jul*_*hke 11

挑剔:根据定义,包含空白字符的字符串不是URI.所以你要找的是实现RFC 3986第2.1节中定义的URI转义的代码.

  • 好点子.以及如何在Java中高效地完成这项工作? (4认同)

小智 11

不幸的是,org.apache.commons.httpclient.util.URIUtil已弃用,并且replacement org.apache.commons.codec.net.URLCodec编码适用于表单帖子,而不适用于实际的URL.所以我必须编写自己的函数,它只执行单个组件(不适用于具有?和&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);
  }
}
Run Code Online (Sandbox Code Playgroud)


Cug*_*uga 8

如果有人不想在项目中添加依赖项,这些函数可能会有所帮助.

我们将URL的"路径"部分传递到此处.您可能不希望将完整的URL作为参数传递(查询字符串需要不同的转义等).

/**
 * 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;
}
Run Code Online (Sandbox Code Playgroud)

并测试:

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

}
Run Code Online (Sandbox Code Playgroud)


Bra*_*ugh 7

正如您遗憾地发现的那样,URLEncoding可以很好地编码HTTP URL.您传入的字符串" http://search.barnesandnoble.com/booksearch/first book.pdf"已正确完整地编码为URL编码形式.你可以传递你在URL中作为参数返回的整个长串gobbledigook,它可以被解码回你传入的字符串.

听起来你想要做的事情与将整个URL作为参数传递有点不同.根据我收集的内容,您尝试创建一个类似于" http://search.barnesandnoble.com/booksearch/whateverTheUserPassesIn " 的搜索网址.你需要编码的唯一东西是"whateverTheUserPassesIn"位,所以你需要做的就是这样:

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

这应该会产生一些对你更有效的东西.

  • 这将用"+"替换userInput中的空格.海报需要用"%20"替换它们. (16认同)

sim*_*nox 7

如果您的URL中有编码的"/"(%2F),则仍然存在问题.

RFC 3986 - 第2.2节说:"如果URI组件的数据与保留字符作为分隔符的目的冲突,那么冲突数据必须在形成URI之前进行百分比编码." (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 
Run Code Online (Sandbox Code Playgroud)


Emi*_*and 7

我阅读以前的答案来编写我自己的方法,因为我无法使用前面的答案的解决方案正常工作,它看起来不错,但如果你能找到不适用于此的URL,请告诉我.

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", "%"));
}
Run Code Online (Sandbox Code Playgroud)


mic*_*123 5

也许可以尝试org.springframework.web.util 中的UriUtils

UriUtils.encodeUri(input, "UTF-8")
Run Code Online (Sandbox Code Playgroud)


To *_*Kra 5

您还可以使用GUAVA路径转义器: UrlEscapers.urlFragmentEscaper().escape(relativePath)