Java 8의 Base64 인코딩 및 디코딩

Java 8은 주로 람다, 스트림, 새로운 날짜 / 시간 모델 및 Nashorn JavaScript 엔진을 Java에 도입 한 것으로 기억 될 것입니다. 일부는 Base64 API와 같은 작지만 유용한 다양한 기능을 도입 한 Java 8을 기억할 것입니다. Base64 란 무엇이며이 API를 어떻게 사용합니까? 이 게시물은 이러한 질문에 대한 답변입니다.

Base64 란 무엇입니까?

Base64 는 바이너리 데이터를 기수 64 표현으로 변환하여 인쇄 가능한 ASCII 문자열 형식으로 나타내는 바이너리-텍스트 인코딩 체계입니다. 각 Base64 숫자는 정확히 6 비트의 이진 데이터를 나타냅니다.

댓글 문서에 대한 Base64 요청

Base64는 RFC 1421 : 인터넷 전자 메일을위한 개인 정보 보호 향상 : 파트 I : 메시지 암호화 및 인증 절차에서 처음 설명되었지만 이름은 지정되지 않았습니다. 나중에 RFC 2045 : MIME (Multipurpose Internet Mail Extensions) Part One : Format of Internet Message Bodies에서 공식적으로 Base64로 발표되었으며 이후 RFC 4648 : The Base16, Base32 및 Base64 데이터 인코딩에서 다시 검토되었습니다.

Base64는 이메일과 같은 정보 시스템을 통해 전송되는 동안 데이터가 수정되는 것을 방지하는 데 사용되며 이는 8 비트 정리가 아닐 수 있습니다 (8 비트 값을 왜곡 할 수 있음). 예를 들어, 이메일 메시지에 이미지를 첨부하고 이미지가 왜곡되지 않고 다른 쪽 끝에 도착하기를 원합니다. 이메일 소프트웨어 Base64는 이미지를 인코딩하고 아래와 같이 메시지에 상응하는 텍스트를 삽입합니다.

Content-Disposition: inline; filename=IMG_0006.JPG Content-Transfer-Encoding: base64 /9j/4R/+RXhpZgAATU0AKgAAAAgACgEPAAIAAAAGAAAAhgEQAAIAAAAKAAAAjAESAAMAAAABAAYA AAEaAAUAAAABAAAAlgEbAAUAAAABAAAAngEoAAMAAAABAAIAAAExAAIAAAAHAAAApgEyAAIAAAAU AAAArgITAAMAAAABAAEAAIdpAAQAAAABAAAAwgAABCRBcHBsZQBpUGhvbmUgNnMAAAAASAAAAAEA ... NOMbnDUk2bGh26x2yiJcsoBIrvtPe3muBbTRGMdeufmH+Nct4chUXpwSPk/qK9GtJRMWWVFbZ0JH I4rf2dkZSbOjt7hhEzwcujA4I7Gust75pYVwAPpXn+kzNLOVYD7xFegWEKPkHsM/pU1F0NKbNS32 o24sSCOlaaFYLUhjky4x9PSsKL5bJsdWkAz3xirH2dZLy1DM2C44zx1FZqL2PTXY/9k=

그림은이 인코딩 된 이미지가로 시작 /하고로 끝나는 것을 보여줍니다 =. 은 ...내가 간결하게 표시하지 않은 것으로 텍스트를 나타냅니다. 이 예제 나 다른 예제의 전체 인코딩은 원래 이진 데이터보다 약 33 % 더 큽니다.

수신자의 이메일 소프트웨어는 인코딩 된 텍스트 이미지를 Base64 디코딩하여 원본 바이너리 이미지를 복원합니다. 이 예에서는 이미지가 나머지 메시지와 함께 인라인으로 표시됩니다.

Base64 인코딩 및 디코딩

Base64는 간단한 인코딩 및 디코딩 알고리즘에 의존합니다. US-ASCII의 65 자 하위 집합과 함께 작동합니다. 여기서 처음 64 자 각각은 동등한 6 비트 이진 시퀀스에 매핑됩니다. 알파벳은 다음과 같습니다.

Value Encoding Value Encoding Value Encoding Value Encoding 0 A 17 R 34 i 51 z 1 B 18 S 35 j 52 0 2 C 19 T 36 k 53 1 3 D 20 U 37 l 54 2 4 E 21 V 38 m 55 3 5 F 22 W 39 n 56 4 6 G 23 X 40 o 57 5 7 H 24 Y 41 p 58 6 8 I 25 Z 42 q 59 7 9 J 26 a 43 r 60 8 10 K 27 b 44 s 61 9 11 L 28 c 45 t 62 + 12 M 29 d 46 u 63 / 13 N 30 e 47 v 14 O 31 f 48 w (pad) = 15 P 32 g 49 x 16 Q 33 h 50 y

65 번째 문자 ( =)는 Base64로 인코딩 된 텍스트를 정수 크기로 채우는 데 사용됩니다.

부분 집합 속성

이 하위 집합에는 US-ASCII를 포함하여 ISO 646의 모든 버전에서 동일하게 표현되는 중요한 속성이 있으며 하위 집합의 모든 문자는 EBCDIC의 모든 버전에서도 동일하게 표현됩니다.

인코딩 알고리즘은 8 비트 바이트의 입력 스트림을받습니다. 이 스트림은 최상위 비트를 먼저 정렬하는 것으로 가정합니다. 첫 번째 비트는 첫 번째 바이트의 상위 비트이고, 8 번째 비트는이 바이트의 하위 비트입니다.

왼쪽에서 오른쪽으로 이러한 바이트는 24 비트 그룹으로 구성됩니다. 각 그룹은 4 개의 연결된 6 비트 그룹으로 처리됩니다. 각 6 비트 그룹은 64 개의 인쇄 가능한 문자 배열로 인덱싱됩니다. 결과 문자가 출력됩니다.

인코딩되는 데이터의 끝에 24 비트 미만을 사용할 수있는 경우 0 비트가 추가되어 (오른쪽) 정수 6 비트 그룹을 형성합니다. 그런 다음 하나 또는 두 개의 =패드 문자가 출력 될 수 있습니다. 고려해야 할 두 가지 경우가 있습니다.

  • 나머지 1 바이트 :이 바이트에 4 개의 0 비트가 추가되어 2 개의 6 비트 그룹을 형성합니다. 각 그룹은 배열을 인덱싱하고 결과 문자가 출력됩니다. 이 두 문자 다음에 두 개의 =패드 문자가 출력됩니다.
  • 2 개의 나머지 바이트 : 2 개의 0 비트가 두 번째 바이트에 추가되어 3 개의 6 비트 그룹을 형성합니다. 각 그룹은 배열을 인덱싱하고 결과 문자가 출력됩니다. 이 세 문자 다음에 하나의 =패드 문자가 출력됩니다.

인코딩 알고리즘의 작동 방식을 배우기 위해 세 가지 예를 고려해 보겠습니다. 먼저 @!*다음 을 인코딩한다고 가정합니다 .

Source ASCII bit sequences with prepended 0 bits to form 8-bit bytes: @ ! * 01000000 00100001 00101010 Dividing this 24-bit group into four 6-bit groups yields the following: 010000 | 000010 | 000100 | 101010 These bit patterns equate to the following indexes: 16 2 4 42 Indexing into the Base64 alphabet shown earlier yields the following encoding: QCEq

계속해서 입력 시퀀스를 @!다음과 같이 줄입니다.

Source ASCII bit sequences with prepended 0 bits to form 8-bit bytes: @ ! 01000000 00100001 Two zero bits are appended to make three 6-bit groups: 010000 | 000010 | 000100 These bit patterns equate to the following indexes: 16 2 4 Indexing into the Base64 alphabet shown earlier yields the following encoding: QCE An = pad character is output, yielding the following final encoding: QCE=

마지막 예제는 입력 시퀀스를 @다음과 같이 단축합니다 .

Source ASCII bit sequence with prepended 0 bits to form 8-bit byte: @ 01000000 Four zero bits are appended to make two 6-bit groups: 010000 | 000000 These bit patterns equate to the following indexes: 16 0 Indexing into the Base64 alphabet shown earlier yields the following encoding: QA Two = pad characters are output, yielding the following final encoding: QA==

디코딩 알고리즘은 인코딩 알고리즘의 역입니다. 그러나 Base64 알파벳이 아닌 문자 또는 잘못된 숫자의 패드 문자가 감지되면 적절한 조치를 취할 수 있습니다.

Base64 변형

Several Base64 variants have been devised. Some variants require that the encoded output stream be divided into multiple lines of fixed length with each line not exceeding a certain length limit and (except for the last line) being separated from the next line via a line separator (carriage return \r followed by a linefeed \n). I describe the three variants that are supported by Java 8's Base64 API. Check out Wikipedia's Base64 entry for a complete list of variants.

Basic

RFC 4648 describes a Base64 variant known as Basic. This variant uses the Base64 alphabet presented in Table 1 of RFC 4648 and RFC 2045 (and shown earlier in this post) for encoding and decoding. The encoder treats the encoded output stream as one line; no line separators are output. The decoder rejects an encoding that contains characters outside the Base64 alphabet. Note that these and other stipulations can be overridden.

MIME

RFC 2045 describes a Base64 variant known as MIME. This variant uses the Base64 alphabet presented in Table 1 of RFC 2045 for encoding and decoding. The encoded output stream is organized into lines of no more than 76 characters; each line (except the last line) is separated from the next line via a line separator. All line separators or other characters not found in the Base64 alphabet are ignored during decoding.

URL and Filename Safe

RFC 4648 describes a Base64 variant known as URL and Filename Safe. This variant uses the Base64 alphabet presented in Table 2 of RFC 4648 for encoding and decoding. The alphabet is identical to the alphabet shown earlier except that - replaces + and _ replaces /. No line separators are output. The decoder rejects an encoding that contains characters outside the Base64 alphabet.

Base64 encoding is useful in the context of lengthy binary data and HTTP GET requests. The idea is to encode this data and then append it to the HTTP GET URL. If the Basic or MIME variant was used, any + or / characters in the encoded data would have to be URL-encoded into hexadecimal sequences (+ becomes %2B and / becomes %2F). The resulting URL string would be somewhat longer. By replacing + with - and / with _, URL and Filename Safe obviates the need for URL encoders/decoders (and their impacts on the lengths of encoded values). Also, this variant is useful when the encoded data is to be used for a filename because Unix and Windows filenames cannot contain /.

Working with Java's Base64 API

Java 8 introduced a Base64 API consisting of the java.util.Base64 class along with its Encoder and Decoder nested static classes. Base64 presents several static methods for obtaining encoders and decoders:

  • Base64.Encoder getEncoder(): Return an encoder for the Basic variant.
  • Base64.Decoder getDecoder(): Return a decoder for the Basic variant.
  • Base64.Encoder getMimeEncoder(): Return an encoder for the MIME variant.
  • Base64.Encoder getMimeEncoder(int lineLength, byte[] lineSeparator): Return an encoder for a modified MIME variant with the given lineLength (rounded down to the nearest multiple of 4 -- output not separated into lines when lineLength<= 0) and lineSeparator. It throws java.lang.IllegalArgumentException when lineSeparator includes any Base64 alphabet character presented in Table 1 of RFC 2045.

    RFC 2045's encoder, which is returned from the noargument getMimeEncoder() method, is rather rigid. For example, that encoder creates encoded text with fixed line lengths (except for the last line) of 76 characters. If you want an encoder to support RFC 1421, which dicates a fixed line length of 64 characters, you need to use getMimeEncoder(int lineLength, byte[] lineSeparator).

  • Base64.Decoder getMimeDecoder(): Return a decoder for the MIME variant.
  • Base64.Encoder getUrlEncoder(): Return an encoder for the URL and Filename Safe variant.
  • Base64.Decoder getUrlDecoder(): Return a decoder for the URL and Filename Safe variant.

Base64.Encoder presents several threadsafe instance methods for encoding byte sequences. Passing the null reference to one of the following methods results in java.lang.NullPointerException:

  • byte[] encode(byte[] src): Encode all bytes in src to a newly-allocated byte array, which this method returns.
  • int encode(byte[] src, byte[] dst): Encode all bytes in src to dst (starting at offset 0). If dst isn't big enough to hold the encoding, IllegalArgumentException is thrown. Otherwise, the number of bytes written to dst is returned.
  • ByteBuffer encode(ByteBuffer buffer): Encode all remaining bytes in buffer to a newly-allocated java.nio.ByteBuffer object. Upon return, buffer's position will be updated to its limit; its limit won't have been changed. The returned output buffer's position will be zero and its limit will be the number of resulting encoded bytes.
  • String encodeToString(byte[] src): Encode all bytes in src to a string, which is returned. Invoking this method is equivalent to executing new String(encode(src), StandardCharsets.ISO_8859_1).
  • Base64.Encoder withoutPadding(): Return an encoder that encodes equivalently to this encoder, but without adding any padding character at the end of the encoded byte data.
  • OutputStream wrap(OutputStream os): 바이트 데이터 인코딩을 위해 출력 스트림을 래핑합니다. 사용 후 반환 된 출력 스트림을 즉시 닫는 것이 좋습니다. 그 동안 가능한 모든 남은 바이트를 기본 출력 스트림으로 플러시합니다. 반환 된 출력 스트림을 닫으면 기본 출력 스트림이 닫힙니다.

Base64.Decoder바이트 시퀀스를 디코딩하는 여러 스레드 세이프 인스턴스 메서드를 제공합니다. 다음 메서드 중 하나에 null 참조를 전달하면 다음과 같은 결과가 발생합니다 NullPointerException.