Java의 HTTP URL 주소 인코딩


366

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으로 대체 됨)

URLEncoderHTTP URL을 인코딩하도록 설계되지 않은 것 같습니다 . JavaDoc에 "HTML 양식 인코딩을위한 유틸리티 클래스"라고 표시되어 있습니다. 다른 방법이 있습니까?



동작이 완전히 맞습니다. URL 인코딩은 URL 매개 변수로 안전하게 전달 될 수 있고 URL로 해석되지 않는 문자열로 변환하는 것입니다. 반면 URL의 작은 부분 하나만 변환하기를 원합니다.
Stephen Holt

답변:


303

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

(URI의 단일 인수 생성자는 잘못된 문자를 이스케이프하지 않습니다)


위의 코드로 잘못된 문자 만 이스케이프 처리합니다. 비 ASCII 문자는 이스케이프하지 않습니다 (fathi의 설명 참조).
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();

13
여기에 언급 된 URI 클래스는 "java.net"이 아닌 "org.apache.commons.httpclient.URI"에서 가져온 것입니다. "java.net"은 잘못된 문자를 허용하지 않습니다. 아래 매트 주석에서 언급 한 방법과 같은 구성 요소에서 URL을 구축 생성자
모하메드 Faramawi

7
@Mohamed : 내가 언급하고 실제로 테스트에 사용한 클래스 java.net.URI는 완벽하게 작동했습니다 (Java 1.6). 표준 Java 클래스가 아니고 링크가의 문서를 가리키는 경우 정규화 된 클래스 이름을 언급합니다 java.net.URI. 그리고 Sudhakar의 의견에 의해 "공동 도서관"을 포함하지 않고 문제를 해결했습니다!
user85421 2016 년

1
URI uri = 새 URI ( "http", "search.barnesandnoble.com", "/ booksearch / é", null); 이 샘플에서 올바르게 이스케이프하지 않습니까? 이것은 % 이스케이프로 탈출했을 것입니다
fmucar

@fatih-맞습니다, 감사합니다! 일반적으로 그것은 문제가되지 않지만 간단한 해결책이 있습니다-이전에 쓴 것과 거의 같습니다. 2 차 편집을 참조하십시오.
user85421

편집을위한 @Carlos Thx. 이제는 탈출하지만 올바른 탈출은 아닙니다. 그것은 é 문자를 의미 경로 PARAMS에 대한 문자의 HEX 값에 %를 추가해야한다는 %의 E9로 변환한다
fmucar

91

위의 답변 대부분이 부정확하다는 점에 유의하십시오.

URLEncoder클래스는, 이름이에도 불구하고, 여기에 필요한 것을 아니다. 썬이이 클래스를 그렇게 성가신 것으로 명명 한 것은 불행합니다. URLEncoderURL 자체를 인코딩하는 것이 아니라 매개 변수로 데이터를 전달하기위한 것입니다.

즉, "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

2
맞습니다. 문서 docs.oracle.com/javase/1.4.2/docs/api/java/net/… , java.lang.String, java.lang.String, int 에 따라 URI 생성자가 이미 쿼리 문자열을 인코딩합니다. , java.lang.String, java.lang.String, java.lang.String)
madoke

8
@Draemon 정답은 맞지만 쿼리 문자열을 드문 방식으로 사용합니다. 보다 일반적인 예는입니다 query = URLEncoder.encode(key) + "=" + URLEncoder.encode(value). 문서는 단지 "법적 URI 문자가 아닌 모든 문자가 인용된다"고 말합니다.
tc.

1
나는 여기 Matt에 동의합니다. 브라우저에 " google.com/help/me/book name + me /? MY CRZY QUERY! + & + :)" URL을 입력 하면 공백이 자동으로 인코딩되지만 "&"가 쿼리 값으로 사용됩니다. 구분 기호와 "+"가 손실됩니다.
arcot

80

여기에 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();

이 특정 URL에서 요청에 사용할 수 있도록 인코딩 된 공간이 필요하다는 것을 알 수 있습니다.

Android 클래스에서 사용할 수있는 몇 가지 기능을 활용합니다. 첫째, URL 클래스는 URL을 적절한 구성 요소로 나눌 수 있으므로 문자열 검색 / 바꾸기 작업을 수행 할 필요가 없습니다. 둘째,이 방법은 단일 문자열이 아닌 구성 요소를 통해 URI를 구성 할 때 구성 요소를 올바르게 이스케이프 처리하는 URI 클래스 기능을 활용합니다.

이 방법의 장점은 유효한 URL 문자열을 가져 와서 특별한 지식 없이도 작동시킬 수 있다는 것입니다.


3
좋은 접근 방법이지만이 코드는 이중 인코딩을 방지하지 못합니다 . 예를 들어 % 20은 % 2520으로 인코딩되었습니다. 스캇의 대답 은 이것으로 고통받지 않습니다.
nattster

2
처리 할 수 ​​없습니다 #.
Alston

또는 경로 인용을 원할 경우 : new URI (null, null, "/ path with spaces", null, null) .toString ()
user1050755

1
@Stallman 파일 이름에 #이 포함 된 경우 URL 클래스는이를 "ref"(URI 클래스의 "fragment"와 동일)에 넣습니다. URL.getRef ()가 경로의 일부로 취급 될 수있는 것을 리턴하는지 여부를 감지하고 URL.getPath () + "#"+ URL.getRef ()를 "path"매개 변수로 전달하고 "fragment"로 널을 전달할 수 있습니다. URI 클래스 7 매개 변수 생성자의 매개 변수입니다. 기본적으로 # 뒤의 문자열은 참조 (또는 앵커)로 처리됩니다.
gouessej

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

}

3
또한 URL을 조각으로 나눠야합니다. 컴퓨터가 URL의 어떤 부분을 인코딩할지 알 방법이 없습니다. 위의 편집 참조
fmucar

4
@fmucar 해당 코드에 감사드립니다! 이것은 UTF-8이 아닙니다. UTF-8을 얻으려면 입력을 사전 처리하십시오 String utf8Input = new String(Charset.forName("UTF-8").encode(input).array());( 여기 에서
가져옴

1
이 솔루션은 실제로 "http : //"부분을 "http % 3A % 2F % 2F"로 인코딩하여 초기 질문을 피하려고 시도했습니다.
Benjamin Piette

2
전체 URL이 아닌 인코딩해야 할 내용 만 전달합니다. 하나의 전체 URL 문자열을 전달하고 올바른 인코딩을 기대할 수있는 방법이 없습니다. 모든 경우에 URL을 논리적 부분으로 분리해야합니다.
fmucar 2016 년

2
안전하지 않은 문자를 UTF-8로 인코딩하지 않기 때문에이 답변에 문제가있었습니다. 그러나 피어 응용 프로그램에 따라 다를 수 있습니다.
Tarnschaf

36

URL이 있으면이 메소드에 url.toString ()을 전달할 수 있습니다. 이중 인코딩을 피하기 위해 먼저 디코딩합니다 (예를 들어, 공백을 인코딩하면 % 20이 발생하고 백분율 기호를 인코딩하면 % 25가 발생하므로 이중 인코딩은 공백을 % 2520으로 바꿉니다). 그런 다음 위에서 설명한대로 URL의 모든 부분을 추가하여 URI를 사용하십시오 (쿼리 매개 변수를 삭제하지 않도록).

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

1
문자열을 " google.co.in/search?q=123%!123 " 으로 전달하면 URLDecoder.decode (string, "UTF-8")가 IllegalArgumentException과 함께 실패합니다 . 유효한 URL입니다. %가 인코딩 문자 대신 데이터로 사용되는 경우이 API가 작동하지 않는 것 같습니다.
MediumOne

26

예, URL 인코딩은 해당 문자열을 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이 필요합니다.


사이드 노트 아파치 커먼즈는 4.x 브랜치에서 URIUtil 관리를 중단했다. 대신 JDK의 URI 클래스를 사용할 것을 권장한다. 문자열을 스스로 분해해야한다는 것을 의미합니다.
Nicholi

2) 정확히 여기도 제안됩니다 stackoverflow.com/questions/5330104/… 또한 URIUtil솔루션 을 사용했습니다
Kra

11

Nitpicking : 정의에 따라 공백 문자가 포함 된 문자열은 URI가 아닙니다. 그래서 당신이 찾고있는 것은 RFC 3986 섹션 2.1에 정의 된 URI 이스케이프를 구현하는 코드입니다 .


우리는 "무엇"이 아니라 대답에 "어떻게"가 필요합니다.
shinzou

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

이 작업을 수행하는 라이브러리가 있어야합니다.
shinzou

9

유감스럽게도 URLEncoding은 HTTP URL을 올바르게 인코딩 할 수 있습니다. 전달한 문자열 " http://search.barnesandnoble.com/booksearch/first book.pdf"에 은 URL 인코딩 형식으로 정확하고 완벽하게 인코딩되었습니다. URL에서 매개 변수로 얻은 gobbledigook의 전체 긴 문자열을 전달할 수 있으며 전달 한 문자열로 정확하게 디코딩 될 수 있습니다.

전체 URL을 매개 변수로 전달하는 것과는 조금 다른 것을하고 싶은 것 같습니다. 내가 수집 한 내용에서 " http://search.barnesandnoble.com/booksearch/whateverTheUserPassesIn " 과 같은 검색 URL을 만들려고합니다 . 인코딩해야하는 것은 "whateverTheUserPassesIn"비트뿐이므로 다음과 같이하면됩니다.

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

그것은 당신에게 더 유효한 것을 생산해야합니다.


17
그러면 userInput의 공백이 "+"로 바뀝니다. 포스터를 "% 20"(으)로 교체해야합니다.
vocaro

@vocaro : 아주 좋은 지적입니다. 인수와 같은 URLEncoder 이스케이프는 나머지 URL이 아닌 쿼리 매개 변수입니다.
Brandon Yarbrough

9

프로젝트에 종속성을 추가하지 않으려는 경우 이러한 기능이 도움이 될 수 있습니다.

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

그리고 테스트 :

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

}

이것에 감사드립니다. 그러나 공백을 인코딩하려면 어떻게해야합니까-> 예제에 따라 대신 % 20을 사용합니까?
N00b Pr0grammer

공백을 % 20으로 설명하기 위해 업데이트
Cuga

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'[...]을 허용합니다.

URL에서 경로 구분 기호 처리를 추가로 제어 할 수 있도록 Tomcat에 다음 Java 시스템 특성이 추가되었습니다 (두 옵션 모두 기본값은 false 임).

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

모든 URL이 프록시 서버에서와 같이 Tomcat에 의해 처리되도록 보장 할 수 없기 때문에 Tomcat은 컨텍스트 액세스를 제한하는 프록시가 사용되지 않는 것처럼 항상 보안되어야합니다.

영향 : 6.0.0-6.0.9

따라서 % 2F 문자가 포함 된 URL이 있으면 Tomcat은 "400 Invalid URI : noSlash"를 반환합니다.

Tomcat 시작 스크립트에서 버그 수정을 전환 할 수 있습니다.

set JAVA_OPTS=%JAVA_OPTS% %LOGGING_CONFIG%   -Dorg.apache.tomcat.util.buf.UDecoder.ALLOW_ENCODED_SLASH=true 

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

4

매트에 동의합니다. 실제로 튜토리얼에서 잘 설명하지는 못했지만 한 가지 중요한 것은 URL 경로를 인코딩하는 방법과 매우 다른 방법은 URL에 추가 된 매개 변수 ( "? "기호). 그들은 비슷한 인코딩을 사용하지만 동일하지는 않습니다.

특히 공백 문자의 인코딩에 적합합니다. URL 경로는 % 20으로 인코딩되어야하지만 쿼리 부분은 % 20 및 "+"기호를 허용합니다. 가장 좋은 아이디어는 웹 브라우저를 사용하여 웹 서버에 대해 스스로 테스트하는 것입니다.

두 경우 모두 항상 COMPONENT BY COMPONENT를 인코딩 하지만 전체 문자열 은 인코딩 하지 않습니다. 실제로 URLEncoder는 쿼리 부분에 대해이를 허용합니다. 경로 부분의 경우 클래스 URI를 사용할 수 있지만이 경우 단일 구성 요소가 아닌 전체 문자열을 요청합니다.

어쨌든, 나는 이러한 문제를 피하는 가장 좋은 방법은 개인적인 비합리적인 디자인을 사용하는 것이라고 믿습니다 . 어떻게? 예를 들어, aZ, AZ, 0-9 및 _ 이외의 다른 문자를 사용하여 디렉토리 또는 매개 변수의 이름을 지정하지 않습니다. 이렇게하면 사용자 입력에서 올 수 있고 사용 된 문자를 알 수 없으므로 모든 매개 변수의 값을 인코딩하면됩니다.


2
질문에 URL을 사용하는 샘플 코드는 답을 입력하는 것이 좋습니다.
Martin Serrano

3

org.springframework.web.util에서 UriUtils 를 사용해 볼 수 있습니다

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

3

GUAVA경로 이스케이프를 사용 하고 사용할 수도 있습니다 . UrlEscapers.urlFragmentEscaper().escape(relativePath)


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

2

위의 내용을 가져 와서 조금 변경했습니다. 나는 긍정적 인 논리를 먼저 좋아하고 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);
}

1

다음 표준 Java 솔루션을 사용하십시오 ( Web Plattform Tests에서 제공하는 약 100 개의 테스트 케이스를 통과 함 ).

0. URL이 이미 인코딩되어 있는지 테스트합니다 .

1. URL을 구조 부분으로 분할 하십시오 . java.net.URL 그것을 위해 사용하십시오 .

2. 각 구조 부분을 올바르게 인코딩하십시오!

3. 사용 IDN.toASCII(putDomainNameHere) 퓨니 코드는 호스트 이름을 인코딩!

4.java.net.URI.toASCIIString() NFC 인코딩 유니 코드를 퍼센트 인코딩하는 데 사용 합니다 (NFKC가 더 좋습니다).

https://stackoverflow.com/a/49796882/1485527에서 자세한 내용을 확인하십시오.


0

HTTP URL을 구성하는 데 도움이되는 새 프로젝트를 만들었습니다. 라이브러리는 경로 세그먼트 및 쿼리 매개 변수를 자동으로 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


0

나는 같은 문제가 있었다. unsing하여 이것을 해결했습니다.

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

문자열을 인코딩하지만 ":"및 "/"는 건너 뜁니다.


0

나는 이것을 사용한다

org.apache.commons.text.StringEscapeUtils.escapeHtml4("my text % & < >");

이 의존성을 추가

 <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-text</artifactId>
        <version>1.8</version>
    </dependency>

-2

이 목적에 맞는 라이브러리를 개발합니다 : galimatias . 웹 브라우저와 동일한 방식으로 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 에서 확인할 수 있습니다.


-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


1
이 답변은 URLCodec없이 불완전합니다.
Lorne의 후작

체인, 이상적 아니다) (.replace에 대한 upvote에 있지만 기본 임시 사용의 경우에 충분하다
svarog


-7

어때요 :

공개 문자열 UrlEncode (문자열 in_) {

String retVal = "";

try {
    retVal = URLEncoder.encode(in_, "UTF8");
} catch (UnsupportedEncodingException ex) {
    Log.get().exception(Log.Level.Error, "urlEncode ", ex);
}

return retVal;

}


URLEncoder를 사용하여 ivalid URL 문자를 이스케이프 처리 할 수 ​​없습니다. 양식 만 인코딩합니다.
Archer
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.