Encoding dan decoding Base64 di Java 8

Java 8 akan diingat terutama untuk memperkenalkan lambda, stream, model tanggal / waktu baru, dan mesin JavaScript Nashorn ke Java. Beberapa juga akan mengingat Java 8 untuk memperkenalkan berbagai fitur kecil tapi berguna seperti API Base64. Apa itu Base64 dan bagaimana cara menggunakan API ini? Posting ini menjawab pertanyaan-pertanyaan ini.

Apa itu Base64?

Base64 adalah skema pengkodean biner-ke-teks yang mewakili data biner dalam format string ASCII yang dapat dicetak dengan menerjemahkannya ke dalam representasi radix-64. Setiap digit Base64 mewakili tepat 6 bit data biner.

Permintaan Base64 untuk dokumen komentar

Base64 pertama kali dijelaskan (tetapi tidak disebutkan namanya) di RFC 1421: Peningkatan Privasi untuk Surat Elektronik Internet: Bagian I: Prosedur Enkripsi dan Otentikasi Pesan. Kemudian, secara resmi disajikan sebagai Base64 di RFC 2045: Multipurpose Internet Mail Extensions (MIME) Bagian Satu: Format Badan Pesan Internet, dan kemudian dikunjungi kembali di RFC 4648: Pengkodean Data Base16, Base32, dan Base64.

Base64 digunakan untuk mencegah data diubah saat dalam perjalanan melalui sistem informasi, seperti email, yang mungkin tidak bersih 8-bit (mereka mungkin mengacaukan nilai 8-bit). Misalnya, Anda melampirkan gambar ke pesan email dan ingin gambar sampai di ujung lain tanpa kacau. Perangkat lunak email Anda Base64-mengkodekan gambar dan memasukkan teks yang setara ke dalam pesan, seperti yang diilustrasikan di bawah ini:

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

Ilustrasi menunjukkan bahwa gambar yang dikodekan ini dimulai dengan /dan diakhiri dengan =. The ...menunjukkan teks yang saya belum ditampilkan untuk singkatnya. Perhatikan bahwa keseluruhan pengkodean untuk ini atau contoh lainnya sekitar 33 persen lebih besar dari data biner asli.

Perangkat lunak email penerima akan melakukan decode Base64 gambar tekstual yang disandikan untuk memulihkan gambar biner asli. Untuk contoh ini, gambar akan ditampilkan sejajar dengan pesan lainnya.

Encoding dan decoding Base64

Base64 mengandalkan algoritma encoding dan decoding sederhana. Mereka bekerja dengan 65 karakter subset US-ASCII di mana masing-masing dari 64 karakter pertama dipetakan ke urutan biner 6-bit yang setara. Ini alfabetnya:

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

Karakter ke-65 ( =) digunakan untuk memasukkan teks yang dikodekan Base64 ke ukuran integral seperti yang akan dijelaskan sebentar lagi.

Properti subset

Subset ini memiliki properti penting yang direpresentasikan secara identik di semua versi ISO 646, termasuk US-ASCII, dan semua karakter dalam subset juga direpresentasikan secara identik di semua versi EBCDIC.

Algoritme pengkodean menerima aliran input byte 8-bit. Aliran ini dianggap diurutkan dengan bit paling signifikan terlebih dahulu: bit pertama adalah bit orde tinggi dalam byte pertama, bit kedelapan adalah bit orde rendah dalam byte ini, dan seterusnya.

Dari kiri ke kanan, byte ini disusun menjadi grup 24-bit. Setiap grup diperlakukan sebagai empat grup 6-bit yang digabungkan. Setiap grup 6-bit mengindeks ke dalam larik 64 karakter yang dapat dicetak; karakter yang dihasilkan adalah keluaran.

Ketika kurang dari 24 bit tersedia di akhir data yang dikodekan, nol bit ditambahkan (di sebelah kanan) untuk membentuk bilangan integral dari grup 6-bit. Kemudian, satu atau dua =karakter pad dapat dikeluarkan. Ada dua kasus yang perlu dipertimbangkan:

  • Satu byte tersisa: Empat bit nol ditambahkan ke byte ini untuk membentuk dua grup 6-bit. Setiap grup mengindeks array dan karakter yang dihasilkan adalah output. Mengikuti dua karakter ini, dua =karakter pad akan dikeluarkan.
  • Dua byte tersisa: Dua bit nol ditambahkan ke byte kedua untuk membentuk tiga grup 6-bit. Setiap grup mengindeks array dan karakter yang dihasilkan adalah output. Mengikuti tiga karakter ini, satu =karakter pad adalah keluaran.

Mari pertimbangkan tiga contoh untuk mempelajari cara kerja algoritma encoding. Pertama, misalkan kita ingin menyandikan @!*:

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

Kami akan melanjutkan dengan mempersingkat urutan input menjadi @!:

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=

Contoh terakhir mempersingkat urutan input menjadi @:

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==

Algoritma decoding adalah kebalikan dari algoritma encoding. Namun, Anda bebas mengambil tindakan yang sesuai setelah mendeteksi karakter yang tidak ada dalam alfabet Base64 atau jumlah karakter pad yang salah.

Varian Base64

Beberapa varian Base64 telah dirancang. Beberapa varian mengharuskan aliran keluaran yang dikodekan dibagi menjadi beberapa baris dengan panjang tetap dengan setiap baris tidak melebihi batas panjang tertentu dan (kecuali untuk baris terakhir) dipisahkan dari baris berikutnya melalui pemisah baris (carriage return \rdiikuti oleh linefeed \n). Saya menjelaskan tiga varian yang didukung oleh API Base64 Java 8. Lihat entri Base64 Wikipedia untuk daftar lengkap varian.

Dasar

RFC 4648 menjelaskan varian Base64 yang dikenal sebagai Basic . Varian ini menggunakan alfabet Base64 yang disajikan dalam Tabel 1 dari RFC 4648 dan RFC 2045 (dan ditampilkan sebelumnya dalam posting ini) untuk encoding dan decoding. Pembuat enkode memperlakukan aliran keluaran yang dikodekan sebagai satu baris; tidak ada pemisah jalur yang dihasilkan. Dekoder menolak pengkodean yang berisi karakter di luar alfabet Base64. Perhatikan bahwa ini dan ketentuan lainnya dapat dikesampingkan.

PANTOMIM

RFC 2045 mendeskripsikan varian Base64 yang dikenal sebagai MIME . Varian ini menggunakan alfabet Base64 yang disajikan pada Tabel 1 dari RFC 2045 untuk encoding dan decoding. Aliran keluaran yang dikodekan diatur menjadi baris yang tidak lebih dari 76 karakter; setiap baris (kecuali baris terakhir) dipisahkan dari baris berikutnya melalui pemisah baris. Semua pemisah baris atau karakter lain yang tidak ditemukan dalam alfabet Base64 diabaikan selama decoding.

URL dan Nama File Aman

RFC 4648 menjelaskan varian Base64 yang dikenal sebagai URL dan Filename Safe . Varian ini menggunakan alfabet Base64 yang disajikan pada Tabel 2 dari RFC 4648 untuk encoding dan decoding. Alfabet identik dengan alfabet yang ditunjukkan sebelumnya kecuali yang -menggantikan +dan _menggantikan /. Tidak ada pemisah garis yang dihasilkan. Dekoder menolak pengkodean yang berisi karakter di luar alfabet Base64.

Pengkodean Base64 berguna dalam konteks data biner yang panjang dan permintaan HTTP GET. Idenya adalah untuk menyandikan data ini dan kemudian menambahkannya ke HTTP GET URL. Jika varian Basic atau MIME digunakan, salah satu +atau /karakter dalam data yang disandikan harus dienkode URL ke dalam urutan heksadesimal ( +menjadi %2Bdan /menjadi %2F). String URL yang dihasilkan akan menjadi lebih panjang. Dengan mengganti +dengan -dan /dengan _, URL dan Filename Safe meniadakan kebutuhan akan encoder / decoder URL (dan dampaknya pada panjang nilai yang dienkode). Selain itu, varian ini berguna ketika data yang dikodekan akan digunakan untuk nama file karena nama file Unix dan Windows tidak dapat berisi /.

Bekerja dengan API Base64 Java

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): Bungkus aliran keluaran untuk encoding data byte. Direkomendasikan untuk segera menutup aliran keluaran yang dikembalikan setelah digunakan, selama itu akan membuang semua byte sisa yang mungkin ke aliran keluaran yang mendasarinya. Menutup aliran keluaran yang dikembalikan akan menutup aliran keluaran yang mendasarinya.

Base64.Decodermenyajikan beberapa metode instance threadsafe untuk mendekode urutan byte. Meneruskan referensi null ke salah satu metode berikut akan menghasilkan NullPointerException: