Кодиране и декодиране на Base64 в Java 8

Java 8 ще бъде запомнена главно с въвеждането на ламбда, потоци, нов модел дата / час и JavaScript двигателя на Nashorn в Java. Някои също ще запомнят Java 8 за въвеждането на различни малки, но полезни функции като Base64 API. Какво е Base64 и как да използвам този API? Тази публикация отговаря на тези въпроси.

Какво е Base64?

Base64 е схема за кодиране на двоичен към текст, която представлява двоични данни в печатния формат ASCII за низ, като ги преобразува в представяне на radix-64. Всяка цифра Base64 представлява точно 6 бита двоични данни.

Искане на Base64 за документи за коментари

Base64 е описан за първи път (но не е именуван) в RFC 1421: Подобряване на поверителността за електронна поща в Интернет: Част I: Процедури за шифроване на съобщения и удостоверяване. По-късно той беше официално представен като Base64 в RFC 2045: Многофункционални разширения на интернет поща (MIME) Част първа: Формат на телата за интернет съобщения и впоследствие преразгледан в RFC 4648: Кодирането на данни 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 разчита на прости алгоритми за кодиране и декодиране. Те работят с 65-символен подмножество на US-ASCII, където всеки от първите 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 текст до интегрален размер, както е обяснено накратко.

Свойство на подмножество

Това подмножество има важното свойство, че е представено еднакво във всички версии на ISO 646, включително US-ASCII, и всички символи в подмножеството също са представени еднакво във всички версии на EBCDIC.

Алгоритъмът за кодиране получава входен поток от 8-битови байтове. Предполага се, че този поток е подреден с най-значимия бит първи: първият бит е битът от висок ред в първия байт, осмият бит е битът от нисък ред в този байт и т.н.

От ляво на дясно тези байтове са организирани в 24-битови групи. Всяка група се третира като четири свързани 6-битови групи. Всяка 6-битова група индексира в масив от 64 печатащи се знака; полученият знак се извежда.

Когато в края на кодираните данни са налични по-малко от 24 бита, се добавят нулеви бита (вдясно), за да се образува интегрален брой 6-битови групи. След това =може да се изведат един или два символа на подложката. Има два случая, които трябва да се разгледат:

  • Един оставащ байт: Четири нулеви бита се добавят към този байт, за да образуват две 6-битови групи. Всяка група индексира масива и се извежда резултиращ символ. След тези два =знака се извеждат два знака на подложката.
  • Два оставащи байта: Два нулеви бита се добавят към втория байт, за да образуват три 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

Създадени са няколко варианта на Base64. Някои варианти изискват кодираният изходен поток да бъде разделен на множество линии с фиксирана дължина, като всеки ред не надвишава определена граница на дължината и (с изключение на последния ред) да бъде отделен от следващия ред чрез разделител на линии (връщане на карета, \rпоследвано от подаване на линия \n). Описвам трите варианта, които се поддържат от Base64 API на Java 8. Вижте записа на Basepedia на Wikipedia за пълен списък с варианти.

Основен

RFC 4648 описва вариант Base64, известен като Basic . Този вариант използва азбуката Base64, представена в Таблица 1 на RFC 4648 и RFC 2045 (и показана по-рано в тази публикация) за кодиране и декодиране. Кодерът третира кодирания изходен поток като един ред; не се извеждат разделители на редове. Декодерът отхвърля кодиране, което съдържа знаци извън азбуката Base64. Имайте предвид, че тези и други условия могат да бъдат отменени.

MIME

RFC 2045 описва вариант Base64, известен като MIME . Този вариант използва азбуката Base64, представена в таблица 1 на RFC 2045 за кодиране и декодиране. Кодираният изходен поток е организиран в редове с не повече от 76 знака; всеки ред (с изключение на последния ред) се отделя от следващия ред чрез разделител на редове. Всички разделители на редове или други символи, които не са намерени в азбуката Base64, се игнорират по време на декодирането.

URL и име на файл в безопасност

RFC 4648 описва вариант на Base64, известен като URL и Filename Safe . Този вариант използва азбуката Base64, представена в Таблица 2 на RFC 4648 за кодиране и декодиране. Азбуката е идентична с азбуката, показана по-рано, с изключение на тази, която -замества +и _замества /. Не се извеждат разделители на редове. Декодерът отхвърля кодиране, което съдържа знаци извън азбуката Base64.

Кодирането Base64 е полезно в контекста на дълги двоични данни и HTTP GET заявки. Идеята е да се кодират тези данни и след това да се добавят към HTTP GET URL. Ако се използва основният вариант или вариант MIME, всеки +или /символите в кодираните данни ще трябва да бъдат кодирани по URL в шестнадесетични последователности ( +става %2Bи /става %2F). Полученият URL низ ще бъде малко по-дълъг. Чрез замяна +с -и /с _, URL и Filename Safe премахва необходимостта от кодиращи / декодиращи URL адреси (и тяхното въздействие върху дължините на кодираните стойности). Също така този вариант е полезен, когато кодираните данни трябва да се използват за име на файл, тъй като имената на файловете на Unix и Windows не могат да съдържат /.

Работа с Java 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представя няколко метода за безопасен екземпляр за декодиране на байтови последователности. Предаването на нулевата препратка към един от следните методи води до NullPointerException: