Pengekodan dan penyahkodan Base64 di Java 8

Java 8 akan dikenang terutama untuk memperkenalkan lambdas, aliran, model tarikh / waktu baru, dan mesin JavaScript Nashorn ke Java. Beberapa juga akan mengingat Java 8 kerana memperkenalkan pelbagai ciri kecil tetapi berguna seperti Base64 API. Apa itu Base64 dan bagaimana saya menggunakan API ini? Catatan ini menjawab soalan-soalan ini.

Apa itu Base64?

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

Permintaan Base64 untuk dokumen komen

Base64 pertama kali dijelaskan (tetapi tidak disebutkan namanya) dalam RFC 1421: Peningkatan Privasi untuk Surat Elektronik Internet: Bahagian I: Prosedur Penyulitan dan Pengesahan Mesej. Kemudian, ia secara rasmi disajikan sebagai Base64 dalam RFC 2045: Sambungan Mel Internet Serbaguna (MIME) Bahagian Pertama: Format Badan Mesej Internet, dan kemudian dikaji semula dalam RFC 4648: Pengkodan Data Base16, Base32, dan Base64.

Base64 digunakan untuk mencegah data diubah ketika dalam perjalanan melalui sistem maklumat, seperti e-mel, yang mungkin tidak bersih 8-bit (mereka mungkin mengotori nilai 8-bit). Sebagai contoh, anda melampirkan gambar ke mesej e-mel dan mahu gambar itu sampai di hujung yang lain tanpa kacau-bilau. Perisian e-mel anda Base64-menyandikan gambar dan memasukkan teks yang setara ke dalam mesej, seperti yang digambarkan di bawah:

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 bahawa gambar yang dikodkan ini bermula dengan /dan berakhir dengan =. Yang ...menunjukkan teks yang saya tidak dipaparkan untuk keringkasan. Perhatikan bahawa keseluruhan pengekodan untuk ini atau contoh lain kira-kira 33 peratus lebih besar daripada data binari asal.

Perisian e-mel penerima akan Base64-menyahkod gambar teks yang dikodkan untuk memulihkan gambar binari yang asal. Untuk contoh ini, gambar akan ditunjukkan sebaris dengan selebihnya mesej.

Pengekodan dan penyahkodan Base64

Base64 bergantung pada algoritma pengekodan dan penyahkodan sederhana. Mereka berfungsi dengan subset 65-karakter AS-ASCII di mana setiap 64 aksara pertama memetakan urutan binari 6-bit yang setara. Inilah abjad:

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 dikodkan Base64 ke ukuran yang tidak terpisahkan seperti yang dijelaskan sebentar lagi.

Harta subset

Subset ini mempunyai sifat penting yang diwakili secara sama dalam semua versi ISO 646, termasuk AS-ASCII, dan semua watak dalam subset juga ditunjukkan secara sama dalam semua versi EBCDIC.

Algoritma pengekodan menerima aliran input 8-bit bait. Aliran ini dianggap disusun dengan bit paling signifikan pertama: bit pertama adalah bit pesanan tinggi dalam bait pertama, bit kelapan adalah bit pesanan rendah dalam bait ini, dan seterusnya.

Dari kiri ke kanan, bait ini disusun dalam kumpulan 24-bit. Setiap kumpulan diperlakukan sebagai empat kumpulan 6-bit yang digabungkan. Setiap kumpulan 6-bit mengindeks ke dalam array dari 64 aksara yang boleh dicetak; watak yang dihasilkan adalah output.

Apabila kurang dari 24 bit tersedia di akhir data yang dikodkan, sifar bit ditambahkan (di sebelah kanan) untuk membentuk bilangan integral kumpulan 6-bit. Kemudian, satu atau dua =watak pad mungkin dihasilkan. Terdapat dua kes yang perlu dipertimbangkan:

  • Satu bait tersisa: Empat bit sifar ditambahkan ke bait ini untuk membentuk dua kumpulan 6-bit. Setiap kumpulan mengindeks array dan watak yang dihasilkan adalah output. Berikutan dua watak ini, dua =watak pad dihasilkan.
  • Dua bait yang tinggal: Dua bit sifar ditambahkan ke bait kedua untuk membentuk tiga kumpulan 6-bit. Setiap kumpulan mengindeks array dan watak yang dihasilkan adalah output. Mengikuti tiga watak ini, satu =watak pad adalah output.

Mari kita pertimbangkan tiga contoh untuk mengetahui bagaimana algoritma pengekodan berfungsi. Pertama, andaikan kita ingin mengekod @!*:

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 meneruskan dengan memendekkan urutan input kepada @!:

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 memendekkan urutan input kepada @:

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 penyahkodan adalah kebalikan dari algoritma pengekodan. Walau bagaimanapun, adalah bebas untuk mengambil tindakan yang sewajarnya setelah mengesan watak yang tidak terdapat dalam huruf Base64 atau bilangan pad yang salah.

Varian Base64

Beberapa varian Base64 telah dirancang. Beberapa varian mensyaratkan bahawa aliran output yang dikodkan dibahagikan kepada beberapa baris panjang tetap dengan setiap baris tidak melebihi had panjang tertentu dan (kecuali untuk baris terakhir) dipisahkan dari baris seterusnya melalui pemisah garis (pengembalian kereta \rdiikuti oleh umpan baris \n). Saya menerangkan tiga varian yang disokong oleh API Base64 Java 8. Lihat entri Base64 Wikipedia untuk senarai lengkap varian.

Asas

RFC 4648 menerangkan varian Base64 yang dikenali sebagai Basic . Varian ini menggunakan abjad Base64 yang ditunjukkan dalam Jadual 1 RFC 4648 dan RFC 2045 (dan ditunjukkan sebelumnya dalam catatan ini) untuk pengekodan dan penyahkodan. Pengekod memperlakukan aliran output yang dikodkan sebagai satu baris; tiada pemisah garis yang dikeluarkan. Penyahkod menolak pengekodan yang mengandungi watak di luar abjad Base64. Perhatikan bahawa ketetapan ini dan lain-lain boleh dibatalkan.

MIME

RFC 2045 menerangkan varian Base64 yang dikenali sebagai MIME . Varian ini menggunakan abjad Base64 yang ditunjukkan dalam Jadual 1 RFC 2045 untuk pengekodan dan penyahkodan. Aliran output yang dikodkan disusun dalam baris tidak lebih daripada 76 aksara; setiap baris (kecuali baris terakhir) dipisahkan dari baris seterusnya melalui pemisah garis. Semua pemisah baris atau watak lain yang tidak terdapat dalam abjad Base64 diabaikan semasa penyahkodan.

URL dan Nama Fail Selamat

RFC 4648 menerangkan varian Base64 yang dikenali sebagai URL dan Nama Fail Selamat . Varian ini menggunakan abjad Base64 yang ditunjukkan dalam Jadual 2 RFC 4648 untuk pengekodan dan penyahkodan. Huruf itu sama dengan abjad yang ditunjukkan sebelumnya kecuali yang -menggantikan +dan _menggantikan /. Tidak ada pemisah garis yang dikeluarkan. Penyahkod menolak pengekodan yang mengandungi watak di luar abjad Base64.

Pengekodan Base64 berguna dalam konteks data binari yang panjang dan permintaan HTTP GET. Ideanya adalah untuk menyandikan data ini dan kemudian menambahkannya ke HTTP GET URL. Sekiranya varian Basic atau MIME digunakan, mana +- mana atau /watak dalam data yang dikodkan harus dikodkan URL ke dalam urutan heksadesimal ( +menjadi %2Bdan /menjadi %2F). Rentetan URL yang dihasilkan agak lama. Dengan mengganti +dengan -dan /dengan _, URL dan Nama Fail Selamat menghilangkan keperluan untuk pengekod / penyahkod URL (dan kesannya pada panjang nilai yang dikodkan). Juga, varian ini berguna ketika data yang dikodekan akan digunakan untuk nama file kerana 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 output untuk mengekod data bait. Dianjurkan untuk segera menutup aliran output yang dikembalikan setelah digunakan, di mana ia akan mengalirkan semua bait sisa yang mungkin ke aliran output yang mendasari. Menutup aliran output yang dikembalikan akan menutup aliran output yang mendasari.

Base64.Decodermembentangkan beberapa kaedah contoh threadsafe untuk menyahkod urutan bait. Menyampaikan rujukan nol ke salah satu kaedah berikut menghasilkan NullPointerException: