Java8でのBase64エンコーディングとデコーディング

Java 8は、主にラムダ、ストリーム、新しい日付/時刻モデル、およびNashornJavaScriptエンジンをJavaに導入するために記憶されます。また、Base64APIなどの小さいながらも便利なさまざまな機能を導入したJava8を覚えている人もいます。Base64とは何ですか?このAPIを使用するにはどうすればよいですか?この投稿はこれらの質問に答えます。

Base64とは何ですか?

Base64は、バイナリデータを基数64表現に変換することにより、印刷可能なASCII文字列形式でバイナリデータを表すバイナリからテキストへのエンコードスキームです。Base64の各桁は、正確に6ビットのバイナリデータを表します。

コメントドキュメントのBase64リクエスト

Base64は、RFC 1421で最初に説明されました(名前は付けられていません)。インターネット電子メールのプライバシー強化:パートI:メッセージの暗号化と認証の手順。その後、RFC 2045:Multipurpose Internet Mail Extensions(MIME)Part One:Format of Internet Message Bodyで正式にBase64として提示され、その後RFC 4648:Base16、Base32、およびBase64 DataEncodingsで再検討されました。

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ビット未満の場合、ゼロビットが追加され(右側)、整数個の6ビットグループが形成されます。次に、1つまたは2つの=パッド文字を出力できます。考慮すべき2つのケースがあります。

  • 残りの1バイト:4つのゼロビットがこのバイトに追加され、2つの6ビットグループが形成されます。各グループは配列にインデックスを付け、結果の文字が出力されます。これらの2文字に続いて、2つの=パッド文字が出力されます。
  • 残りの2バイト:2つのゼロビットが2番目のバイトに追加され、3つの6ビットグループが形成されます。各グループは配列にインデックスを付け、結果の文字が出力されます。これらの3文字に続いて、1つの=パッド文字が出力されます。

エンコーディングアルゴリズムがどのように機能するかを学ぶために、3つの例を考えてみましょう。まず、エンコードしたいとします@!*

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)。Java8のBase64APIでサポートされている3つのバリアントについて説明します。バリアントの完全なリストについては、ウィキペディアのBase64エントリを確認してください。

基本

RFC 4648は、Basicとして知られるBase64バリアントについて説明しています。このバリアントは、RFC4648およびRFC2045の表1に示されている(およびこの投稿の前半に示されている)Base64アルファベットをエンコードおよびデコードに使用します。エンコーダーは、エンコードされた出力ストリームを1行として扱います。行区切り文字は出力されません。デコーダーは、Base64アルファベット以外の文字を含むエンコードを拒否します。これらおよびその他の規定は上書きされる可能性があることに注意してください。

MIME

RFC 2045は、MIMEと呼ばれるBase64バリアントについて説明しています。このバリアントは、RFC2045の表1に示されているBase64アルファベットをエンコードとデコードに使用します。エンコードされた出力ストリームは、76文字以下の行に編成されます。各行(最後の行を除く)は、行区切り記号を介して次の行から分離されます。Base64アルファベットにないすべての行区切り文字またはその他の文字は、デコード中に無視されます。

URLとファイル名は安全

RFC 4648は、URLおよびファイル名セーフと呼ばれるBase64バリアントについて説明しています。このバリアントは、RFC4648の表2に示されているBase64アルファベットをエンコードとデコードに使用します。アルファベットは、-置換+_置換を除いて、前に示したアルファベットと同じです/。行区切り文字は出力されません。デコーダーは、Base64アルファベット以外の文字を含むエンコードを拒否します。

Base64エンコーディングは、長いバイナリデータやHTTPGETリクエストのコンテキストで役立ちます。このデータをエンコードしてから、HTTP GETURLに追加するという考え方です。BasicまたはMIMEバリアントが使用された場合、エンコードされたデータ内の任意の+または/文字は、16進シーケンスにURLエンコードされる必要があります(に+なる%2Bおよびに/なる%2F)。結果のURL文字列はやや長くなります。URLとFilenameSafeをに置き換えること+-、URLエンコーダー/デコーダー(およびエンコードされた値の長さへの影響)が不要になります。また、このバリアントは、UnixおよびWindowsのファイル名にを含めることができないため、エンコードされたデータをファイル名に使用する場合に役立ちます。/_/

JavaのBase64APIの操作

Javaの8は、以下からなるBase64でAPIを導入しjava.util.Base64て一緒にクラスEncoderDecoder入れ子になったstaticクラスを。エンコーダーとデコーダーを取得するためのBase64いくつかのstatic方法を示します。

  • Base64.Encoder getEncoder():Basicバリアントのエンコーダーを返します。
  • Base64.Decoder getDecoder():Basicバリアントのデコーダーを返します。
  • Base64.Encoder getMimeEncoder():MIMEバリアントのエンコーダーを返します。
  • Base64.Encoder getMimeEncoder(int lineLength, byte[] lineSeparator):指定されたlineLength(4の最も近い倍数に切り捨てられた-出力がlineLength<= 0の場合は行に分割されない)変更されたMIMEバリアントのエンコーダーを返しlineSeparatorます。RFC2045の表1に示されているBase64アルファベット文字が含まれているjava.lang.IllegalArgumentException場合にスローlineSeparatorされます。

    noargumentgetMimeEncoder()メソッドから返されるRFC2045のエンコーダーは、かなり堅固です。たとえば、そのエンコーダーは、76文字の固定行長(最後の行を除く)でエンコードされたテキストを作成します。エンコーダでRFC1421をサポートする場合は、64文字の固定行長を示しますgetMimeEncoder(int lineLength, byte[] lineSeparator)。を使用する必要があります。

  • Base64.Decoder getMimeDecoder():MIMEバリアントのデコーダーを返します。
  • Base64.Encoder getUrlEncoder():URLおよびファイル名セーフバリアントのエンコーダーを返します。
  • Base64.Decoder getUrlDecoder():URLおよびファイル名セーフバリアントのデコーダーを返します。

Base64.Encoderバイトシーケンスをエンコードするためのいくつかのスレッドセーフインスタンスメソッドを示します。次のいずれかのメソッドにnull参照を渡すと、次のようになりますjava.lang.NullPointerException

  • byte[] encode(byte[] src)srcこのメソッドが返す、新しく割り当てられたバイト配列にすべてのバイトをエンコードします。
  • int encode(byte[] src, byte[] dst):エンコードのすべてのバイトsrcには、dst(オフセット0から始まります)。dstエンコーディングを保持するのに十分な大きさがない場合は、IllegalArgumentExceptionがスローされます。それ以外の場合は、書き込まれたバイト数dstが返されます。
  • ByteBuffer encode(ByteBuffer buffer):残りのすべてのバイトをbuffer新しく割り当てられたjava.nio.ByteBufferオブジェクトにエンコードします。戻ると、bufferの位置は限界まで更新されます。その制限は変更されていません。返される出力バッファの位置はゼロになり、その制限は結果のエンコードされたバイト数になります。
  • String encodeToString(byte[] src):すべてのバイトをsrc文字列にエンコードします。文字列が返されます。このメソッドを呼び出すことは、を実行することと同じnew String(encode(src), StandardCharsets.ISO_8859_1)です。
  • Base64.Encoder withoutPadding():このエンコーダーと同等にエンコードするエンコーダーを返しますが、エンコードされたバイトデータの最後にパディング文字を追加しません。
  • OutputStream wrap(OutputStream os):バイトデータをエンコードするための出力ストリームをラップします。使用後は、返された出力ストリームをすぐに閉じることをお勧めします。その間、残りのすべてのバイトが基になる出力ストリームにフラッシュされます。返された出力ストリームを閉じると、基になる出力ストリームが閉じます。

Base64.Decoderバイトシーケンスをデコードするためのいくつかのスレッドセーフインスタンスメソッドを示します。次のいずれかのメソッドにnull参照を渡すと、次のようになりますNullPointerException