Kotlin での Base64 エンコード:完全ガイド
Base64 に関する会話の半分は、パックされたデータからバイトを取り戻す話です。もう半分 - ここで正しいサイトに来ているあなたが扱う側 - は、そもそもそのパックされたデータをどうやって産むかという話です。アプリケーションのどこかで、テキストしか理解しないチャネルを通って運ばれなければならないバイトがあります:JSON の文字列、HTTP ヘッダー、メール、URL、設定ファイル。Base64 が定番の答えで、Kotlin には標準ライブラリの中にファーストクラスの答えがあります:kotlin.io.encoding の Base64 クラスで、Kotlin 2.2 以降安定しています。
このガイドは、あなたが Base64 を産む側として実際に必要なものを順番に歩きます:4つのプリセット・スキーム、パディングのつまみ、メールと証明書が従っている行折り返しのルール、URL 安全なアルファベット、バイトはどこから来るのか、そして Kotlin を1つずつ含む実世界のシナリオ集。フォーマット自体 - バイト3つが文字4つになるしくみ、= がどこから来るか - はホームページで扱っているので、ここでは Kotlin にまっすぐ入ります。
1つのクラスと4つのプリセット
API 全体は、kotlin.io.encoding パッケージの単一のクラス Base64 です。encoder オブジェクトを構築することも、ビルダーもありません。代わりに、このクラスには RFC スキームごとに1つのプリセット・インスタンスが4つ同梱されており、最も一般的なものの代わりに黙って立っているコンパニオンオブジェクトが1つあります:
| インスタンス | アルファベット | エンコード時の行折り返し | エンコード時のパディング | 使う対象 |
|---|---|---|---|---|
Base64.Default | + と / | なし | = を出力 | 汎用、API、data URL |
Base64.UrlSafe | - と _ | なし | = を出力(オフにできる) | URL、トークン、JWT |
Base64.Mime | + と / | 76文字ごとに CRLF | = を出力 | メール本文と添付ファイル |
Base64.Pem | + と / | 64文字ごとに CRLF | = を出力 | 証明書と秘密鍵 |
人をつまずかせる命名の詳細:これらはインスタンスであって、ファクトリーではありません。すべてのインスタンスは不変の値で、パディングのような挙動を変更すると、古いものを破壊的に変更するのではなく新しいインスタンスが返ります。これによりプリセットはスレッド間で安全に共有でき、オブジェクトに格納でき、それが理由でクラス全体が内部状態のない単純な値型で済みます。
初めてのエンコード:バイトを投入し、文字列を出す
このサイトの最小限で有用なプログラムがこれです:バイト5つを投入し、8文字の文字列が出てくる。入力は常に ByteArray(またはそのスライス)で、結果はテキストが許されるならどこにでも置けるただの String です:
import kotlin.io.encoding.Base64
fun main() {
val bytes = "Hello".encodeToByteArray()
val packed = Base64.encode(bytes)
println(packed) // SGVsbG8=
}
この1行は、見た目より多くの仕事をしていて、Kotlin は同じ操作のいくつかの形を与えてくれ、どれも関数の名前そのままに読めます:
encode(bytes)はStringを返し、上の形。encodeToByteArray(bytes)は ASCII 文字のByteArrayを返し、パックされた形そのものが別のバッファに入る場合に便利です。encodeIntoByteArray(bytes, destination)は、あなたがすでに割り当てたByteArrayに書き込み、ホットパスで割り当てを1回分節約します。encodeToAppendable(bytes, builder)はAppendableを実装しているものなら何でも追記でき、StringBuilderのように、大きなドキュメントを組み立てているときに自然な組み合わせになります。
4つすべてが同じ任意の startIndex と endIndex の範囲を受け取るため、大きなバッファのスライスを先にコピーせずにパックできます。Base64.Default がコンパニオンオブジェクトなので、インスタンスを落として Base64.encode(bytes) と書くシュガーも使え、両方の形は同じ呼び出しです。
4/3 の法則:どれくらい長くなるか
エンコーダーをリリースする前に、出力がどれだけ大きくなるかを正確に知っておく価値があります。Base64 は、すでに持っていた情報に文字を消費するからです。計算は厳密です:入力バイト3つごとにちょうど出力文字4つになり、残った1バイトや2バイトでも4つのフルグループを消費し、= でグループを埋めます。最初のサイズでの結果はこうです:
| 入力バイト | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
|---|---|---|---|---|---|---|---|---|
| 出力文字 | 4 | 4 | 4 | 8 | 8 | 8 | 12 | 12 |
表の後ろの計算式は 4 * ceil(bytes / 3) です。最悪の場合、バイト1つが文字4つになり、300 パセントの増税。3バイト以上では、回線上のデータがおよそ3分の1増えるところに収束します。これがコストモデルの全体で、インスタンスごとのばらつきはなく、大きなペイロードを Base64 にするのは反射ではなく意図的にやるべきだ、という理由です。
パディングは設定であって宿命ではない
エンコード側では、= の文字は方針の判断で、Kotlin はそれを第一級のものにしています。すべてのインスタンスが PaddingOption を持ち、withPadding がつまみを動かした新しいインスタンスを手渡します。4つのプリセットはすべて PRESENT から始まるため、"Hello" は SGVsbG8 ではなく SGVsbG8= として出てくるのです:
import kotlin.io.encoding.Base64
fun main() {
val bytes = "Hello".encodeToByteArray()
val noPad = Base64.Default.withPadding(Base64.PaddingOption.ABSENT)
println(Base64.encode(bytes)) // SGVsbG8=
println(noPad.encode(bytes)) // SGVsbG8
}
つまみには4つの位置があります。名前の前半がエンコーダーが何を出力するかを決め、後半が、あなたが(または向こう側が)後にこれを逆方向に回したとき、同じインスタンスのデコーダーがどれくらい厳格かを決定します:
| PaddingOption | エンコーダーが = を出力 | デコーダーが = を受理 |
|---|---|---|
PRESENT | はい | 必須、それ以外は失敗 |
ABSENT | いいえ | 禁止、余計なパッドで失敗 |
PRESENT_OPTIONAL | はい | どちらでも |
ABSENT_OPTIONAL | いいえ | どちらでも |
エンコード時に最も一般的な選択は、UrlSafe アルファベットの ABSENT で、まさに JSON Web Token と多くの URL スキームが期待する形です。すぐまた出会います。
Base64url: URL、トークン、JWT
クラシックなアルファベットには + と / が含まれ、どちらも URL では破滅的です:クエリ文字列の + はたいていスペースとして読まれ、/ はパスの区切り記号です。RFC 4648 の第5節が URL 安全な変種を定義し、- と _ に差し替え、Base64.UrlSafe がそのスキームです。クラシックなアルファベットで / を産む同じバイトをエンコードすると、差し替えが実際に動きを見せてくれます:
import kotlin.io.encoding.Base64
fun main() {
val bytes = "Hello?".encodeToByteArray()
println(Base64.encode(bytes)) // SGVsbG8/
println(Base64.UrlSafe.encode(bytes)) // SGVsbG8_
}
正式な実世界のユーザーが JWT で、そのヘッダーとペイロードはドットで結ばれた、パディングなしの base64url です。これが1つを作るエンコード側の半分で、最終トークンをライブラリが署名する場合でも、理解しておくべき形です:
import kotlin.io.encoding.Base64
fun main() {
val header = """{"alg":"HS256","typ":"JWT"}"""
val payload = """{"sub":"1234567890","name":"John Doe"}"""
val noPad = Base64.UrlSafe.withPadding(Base64.PaddingOption.ABSENT)
val h = noPad.encode(header.encodeToByteArray())
val p = noPad.encode(payload.encodeToByteArray())
val token = "$h.$p.Ym9nVXNlZlNpZ25hdHVyZUZvckRlbW8"
println(token)
}
出力は:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIn0.Ym9nVXNlZlNpZ25hdHVyZUZvckRlbW8
ここには警告が2つあります。第一に、第3セグメントは署名で、本物を作るには本物の暗号(JCA/JCE の署名者か JWT ライブラリ)が必要で、手づくりのバイトでは決してありません。上記のスニペットはエンコードの形を示しているだけです。第二に、JVM にいて癖で java.util.Base64.getUrlEncoder() に手を伸ばすなら、それがデフォルトでパディングする点に注意:JWT 風の出力にはそちらで .withoutPadding() が必要です。Kotlin のプリセットも最初から同じくらいの音を立ててパディングし、代わりに withPadding の呼び出しでオプトアウトします。
行折り返し:Mime と Pem プリセット
4つのプリセットのうち2つが、出力を短い行に折り返します。その理由は歴史的ものです。古いメール輸送は長い行を壊したため、RFC 2045 の第6.8節が MIME の base64 を1行76文字までと上限を設定しています。PKI ツールは、より古い PEM の伝統に倣って64を使います。Kotlin は両方のルールをプリセット自体に焼き込んでいます:行区切りは CRLF、改行はちょうど限界の位置に落ち、末尾に最後の区切りはつきません。バイト200のペイロードをそれぞれのラッパーに通すと、こうなります:
import kotlin.io.encoding.Base64
fun main() {
val data = ByteArray(200) { (it % 251).toByte() }
println(Base64.Mime.encode(data).lines().maxOf { it.length }) // 76
println(Base64.Pem.encode(data).lines().maxOf { it.length }) // 64
}
その入力に対して、Mime は4行を、Pem は5行を産みます。最初の Mime 行は:
AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4
覚えておくべき罠は、あなたがコードを書いている方向とは逆の方向のものです:折り返された出力は1行ではない。Mime の出力を厳格な単一行の消費者に渡すと、CRLF がデコードエラーになるため、ラッパーは便利さではなくチャネルで選べばよい。API、data URL、モダンなもの全般には Default が正しいデフォルトで、折り返しはメールと証明書の話です。
バイトはどこから来るのか
エンコーダーは、あなたが渡すバイトと同じだけ正直であり、興味深い判断は encode が呼び出される1手前で行われます。最も一般的な出所はテキストで、最も一般的なミスは、文字コードが黙って決めさせるところです:
text.encodeToByteArray()は、あらゆるプラットフォームで常に UTF-8 です。JSON、メール、ウェブデータには正しい選択で、テキストが Latin-1 や UTF-16 でお向こう側がそれに従ってデコードする場合には間違いになります。- JVM では、インライン拡張の
text.toByteArray(charset)で明示的に選べます。Kotlin 1.0 以来標準ライブラリにあり、Java のgetBytes(charset)に対する Kotlin 側の答えです。kotlin.StringにgetBytesは存在しないので、Kotlin の文字列でtext.getBytes()と書いたらコンパイラが教えてくれます。拡張がその道です。
import kotlin.io.encoding.Base64
fun main() {
val text = "héllo"
println(Base64.encode(text.encodeToByteArray())) // aMOpbGxv
println(Base64.encode(text.toByteArray(Charsets.ISO_8859_1))) // aOlsbG8=
}
同じ5文字が、Base64 が絡む前にバイトが違ったため、2つの違うパックされた形になります。デコーダーが後に UTF-8 を想定するなら、Latin-1 版はもじばけにデコードされ、どちらの端の Base64 の技でも文字コードの不整合は直せません。
他のバイトの出所も同じ形に従います。ファイルなら file.readBytes() か path.readBytes() のあとに encode。予割り当てのバッファには encodeIntoByteArray(bytes, destination)。作っている最中のドキュメントには encodeToAppendable(bytes, builder)、これは目的を返すので呼び出しがビルダーメソッドのように連鎖します:
import kotlin.io.encoding.Base64
fun main() {
val sb = StringBuilder("prefix-")
Base64.encodeToAppendable("Hello".encodeToByteArray(), sb)
println(sb) // prefix-SGVsbG8=
}
そして JVM には、メモリに収まらない入力向けのストリーミング形式があり、まだ実験的で、自分の名前でインポートできます。命名のひねり:encodingWith がラップするのは出力ストリームで、それを介した書き込みは base64 として出て、base64 のバイトが下位のストリームに着地します:
import java.io.ByteArrayOutputStream
import kotlin.io.encoding.Base64
import kotlin.io.encoding.ExperimentalEncodingApi
import kotlin.io.encoding.encodingWith
@OptIn(ExperimentalEncodingApi::class)
fun main() {
val raw = ByteArray(10_000) { (it % 251).toByte() }
val packed = ByteArrayOutputStream()
packed.encodingWith(Base64.Default).use { encoded ->
encoded.write(raw)
}
println(packed.size()) // 13336
}
指針はこうです:収まるものはすべてメモリ内の encode、収まらないストリームには encodingWith、そしてバイトが実際にテキストなら、文字コードは常に明示に。
フィールドノート:HTTP Basic 認証
HTTP Basic 認証はインターネットで最も古い Base64 のユースケースで、サービス間トラフィックでは今も至るところにあります。RFC 7617 がスキームを定義します:ユーザーとパスワードを取り、1つのコロンで結んで、結果を base64 にし、Authorization ヘッダーに Basic とスペースとパックされた文字列で送り出す。Kotlin ではこう:
import kotlin.io.encoding.Base64
fun main() {
val credentials = "alice:s3cr3t"
val header = "Basic " + Base64.encode(credentials.encodeToByteArray())
println(header) // Basic YWxpY2U6czNjcjN0
}
ここで Base64 を使い、より強力なものを使わない理由は?ヘッダーの値は1つの印刷可能なトークンでなければならないからで、Base64 はそれを保証します。率直な警告:Base64 はエンコーディングであって、暗号化ではありません。どのクライアントでも YWxpY2U6czNjcjN0 を1ステップで alice:s3cr3t に逆算できるので、Basic 認証は TLS 接続の上、できれば人間用のパスワードではなくトークン認証情報を使って使うものです。そんなヘッダーを解析するときは、コロンでちょうど1回だけ分割してください。パスワードにコロンが含まれることは法的に許されているからです。
フィールドノート:画像と data URL
Data URL はバイナリ・アセットを HTML、CSS、JSON に直接埋め込み、ブラウザが2回目のリクエストをしないようにします。形は、メディアタイプ、カンマ、base64 という単語、もう1つのカンマ、そしてパックされたバイト:
import kotlin.io.encoding.Base64
fun main() {
val png = byteArrayOf(0x89.toByte(), 0x50, 0x4E, 0x47, 0x0D.toByte(), 0x0A.toByte(), 0x1A.toByte(), 0x0A.toByte())
val dataUrl = "data:image/png;base64," + Base64.encode(png)
println(dataUrl) // data:image/png;base64,iVBORw0KGgo=
}
上のバイトは PNG ファイルの先頭8バイト、つまりすべてのデコーダーがチェックするマジックナンバーです。なぜ Base64 がぴったり合うか:ペイロードはマークアップの中の URL 安全なテキスト・トークンでなければならないからで、Base64 は安定した文法を持つ、広くサポートされているバイナリからテキストへの変換で唯一のものだからです。罠はサイズです。300 キロバイトのロゴはマークアップでおよそ 400 キロバイトになり、余分な 1 キロバイトはそれを含まれるすべてのページ読み込みで支払われます。Data URL はアイコン、アバター、小さなスプライトには優れたツールですが、動画には最悪のツールで、大きな写真にも中途半端なツールです。インライン化の前に測ってください。
フィールドノート:メールの添付ファイル
SMTP はバイナリという概念の登場より前のテキスト・プロトコルなので、あなたが今まで受け取ったすべてのメールのすべての添付ファイルは Base64 で、76 文字で折り返され、Content-Transfer-Encoding: base64 ヘッダーで宣言されています。小さなバイナリ添付を持つ最小の MIME パートは以下のようになります。Kotlin で生成した本文部分は、5 バイトの %PDF- ヘッダー用に埋め込んであります:
From: sender@example.com
To: receiver@example.com
Subject: report
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="cut-here"
--cut-here
Content-Type: text/plain; charset="utf-8"
The quarterly report follows as an attachment.
--cut-here
Content-Type: application/pdf
Content-Transfer-Encoding: base64
Content-Disposition: attachment; filename="report.pdf"
JVBERi0=
--cut-here--
(本文 JVBERi0= は5バイト %PDF- の base64 です。実際のレポートなら複数の76文字行に折り返されます。)ファイルのバイトがあれば、Kotlin 側は1行で:
import kotlin.io.encoding.Base64
fun main() {
val pdf = byteArrayOf(0x25, 0x50, 0x44, 0x46, 0x2D) // "%PDF-"
println(Base64.Mime.encode(pdf)) // JVBERi0=
}
罠はチャネルの規律です。本文には Default ではなく Mime を使ってください。厳格な MIME パーサーは折り返しを期待し、10,000 文字のプレーンな Default 行は一部の輸送に拒否されたり壊されたりします。Content-Transfer-Encoding 行の base64 の大文字小文字をそのまま保ち、折り返しはフォーマットの一部であることを覚えておいてください:折り返しなしの出力と折り返しありの出力は同じバイトの異なる表現で、向こう側のパーサーは自分がどちらを食べているかを知る必要があるからです。
フィールドノート:JSON API とアップロード
API が JSON ドキュメントの中にバイナリを望むとき、慣例は base64 を持つ文字列フィールドで、これはエコシステムで最も便利なパターンの1つです。JSON はすでにテキストの住処を持っているからです。kotlinx.serialization があれば、往復はシンプルです:
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
import kotlin.io.encoding.Base64
@Serializable
data class UploadRequest(val name: String, val payload: String)
fun main() {
val icon = byteArrayOf(0x89.toByte(), 0x50, 0x4E, 0x47)
val request = UploadRequest("icon.png", Base64.encode(icon))
val json = Json.encodeToString(UploadRequest.serializer(), request)
println(json) // {"name":"icon.png","payload":"iVBORw=="}
}
ここで Base64 を使う理由:JSON にバイナリ型がないため、ペイロードはテキストでなければならず、Base64 は、API の消費者がドキュメントなしで認識する、最もサプライズのないバイナリの文法だからです。罠はスケールです。4/3 の増税はすべてのリクエストとレスポンスで支払われ、10 メガバイトのアップロードは 13.3 メガバイトの JSON 文字列になり、あなたのパーサーはそれをメモリに保持して、エスケープして、検証しなければなりません。大きなファイルには、multipart/form-data かバイナリボディがほぼいつも良いワイヤー形式です。JSON 内の base64 は、サムネイル、アイコン、署名、小さな塊のように、便利さが課税を上回るものに温存しましょう。
フィールドノート:設定とコマンドライン
最後の2つのパターンは、あらゆるコードベースに現れる小さなものです。設定値、トークン、ライセンスキー、ときには小さなシークレットは、輸送がテキスト専用で、値に引用符や改行が含まれることがありうるため、base64 で環境変数やプロパティファイルを通して運ばれることがあります。読み戻すのは、同じ2ステップの踊りを逆方向にやるだけです:System.getenv かプロパティの参照、それからデコード。コマンドラインでは、輸送や検査のためにファイルをエンコードするのは10行のプログラムです:
import java.io.File
import kotlin.io.encoding.Base64
fun main(args: Array<String>) {
require(args.isNotEmpty()) { "usage: b64encode <file>" }
val bytes = File(args[0]).readBytes()
val encoded = Base64.encode(bytes)
File(args[0] + ".b64").writeText(encoded)
println("Wrote ${encoded.length} characters to ${args[0]}.b64")
}
バイト10の hello file を含むファイルで実行すると、16文字の aGVsbG8gZmlsZQ== が書かれます。両方のケースの罠は同じ2つです:設定の中の Base64 は金庫ではなく、値はプレーンテキストから1ステップの距離にあり、いずれにせよワイヤーの上ではシークレットとして扱うべきだし、手づくりの CLI ツールはアルファベットを意図的に決めるべきで、あなたの出力を URL にパイプするユーザーには Default ではなく UrlSafe が必要だからです。
エンコード時に何が起き得るか
エンコーディングは内容に寛容です:どんなバイト列でも有効な入力なので、デコーダーにあるような「invalid symbol」の失敗はありません。実際に 例外を投げるのは幾何学で、メッセージは有用になるほど正確です:
| 状況 | 例外 | メッセージ |
|---|---|---|
endIndex が配列の末端を超える | IndexOutOfBoundsException | startIndex: 0, endIndex: 100, size: 5 |
startIndex が endIndex を超える | IllegalArgumentException | startIndex: 3 > endIndex: 2 |
目的配列が encodeIntoByteArray に対して小さすぎる | IndexOutOfBoundsException | The destination array does not have enough capacity, destination offset: 0, destination size: 2, capacity needed: 8 |
これらと並んで、Kotlin 特有の罠が2つあります。1つ目は定番の int + String ミス:bytes.size + " bytes" はコンパイルされません。Int の plus は文字列を連結しないためで、補間形式の "${bytes.size} bytes" が Kotlin 流です。2つ目は文字コードの参照で、JVM が認識しない名前(例えば Charset.forName("utf-9"))には UnsupportedCharsetException を投げるため、文字コード名のタイプミスはコンパイルエラーではなく実行時例外になり、名前がタイプされた場所ではなくエンコーダーが動く場所で顔を上げます。
落とし穴、Kotlin 仕様の
以下の罠は、標準ライブラリに初めて手を伸ばす Kotlin 開発者に特に咬むものです:
encodeToByteArray()に文字コードを選ばせると、Latin-1 や UTF-16 のソースが、デコーダーが読み戻せないバイトにパックされます。それは常に、黙って UTF-8 を選ぶからです。文字コードは意図的に決めてください。UTF-8 でないときは、JVM でtoByteArray(charset)を使います。- 筋肉の記憶で
java.util.Base64に手を伸ばすと、そのgetUrlEncoder()はデフォルトでパディングします。JWT には.withoutPadding()を思い出さない限り間違いの形です。Kotlin のプリセットは両側で選択を明示にします。 MimeかPemの折り返し済み出力を単一行の消費者に渡すと、CRLF は表現の一部で、1行を期待する厳格なデコーダーに失敗します。チャネルが折り返しを期待するときだけ折り返してください。- Kotlin の文字列で
text.getBytes()と書くな。Java のメソッドはkotlin.Stringからは見えず、Kotlin 1.0 以来あるインラインのtoByteArray(charset)拡張が代替です。 - 古いツールチェーンを動かすな。あるディストリビューションのシステム Kotlin はまだ 1.3 で、標準ライブラリの
Base64よりずっと前です。クラスが存在するには 1.8.20 が要り、パディングの制御には 2.0.20、安定には 2.2 が必要です。 - Base64 をセキュリティ層として扱うな。それは公開され、1ステップの逆変換を持つ輸送のエンコーディングです。シークレットなものはパックされる前に暗号化されるべきで、パックされるだけであってはなりません。
選び方の早見ガイド
迷ったら、判断を下さるのはたいていチャネルであって、内容ではありません。短い版本:
Base64.Default:API、JSON、data URL、実質1行のテキストであるものすべてに。パディング付きの出力がワイヤー上で最も互換性の高い形です。Base64.UrlSafe+ABSENTパディング:トークン、JWT、URL セグメントやクエリパラメータに着地するものすべてに。Base64.Mime:メール本文と添付ファイルに。76文字の行はこのフォーマットの必須要件です。Base64.Pem:証明書と秘密鍵に。64文字の行は、すべての PKI ツールが期待するものです。
そのあと、2つの横断する習慣:入力がテキストなら、文字コードは必ず明示にし、4/3 の増税には目を配り、大きなペイロードには base64 のチャネルではなくバイナリのチャネルを与える。
標準化への道
標準ライブラリの Base64 への道は新しすぎて、まだそこがない古い Kotlin に出会うことになります。このクラスが初めて登場したのは 2023 年 4 月の Kotlin 1.8.20 で、実験的とマークされ、3つのインスタンスとよりシンプルな表面を持っていました:エンコードは常にパディングし、少なく求める手段はなかった。文字列の末尾から = を removeSuffix で剥がしている 1.8 世代のコードを見たことがあるなら、それがパディングなし出力に向けた当時の唯一の道具で、今や捨ててよい習慣です。2025 年 6 月リリースの Kotlin 2.2 が API を安定化させ、最後のピース、Pem インスタンスを追加しました。PaddingOption のつまみと withPadding はすでに 2.0 ライン、正確には 2.0.20 に到着しており、どちらの方向にもパディングを第一級に制御する初めての方式でした。ストリーミング・ヘルパーの encodingWith と decodingWith は依然として実験的で JVM 専用で、それは標準ライブラリが API を凍結する前にもっと現場の経験を積ませたいとフラグを立てる方法です。2.4.0 ライン以降、言語は標準ライブラリに18か月のサポートウィンドウも移り、2.4.x のコンパイラにピン留めしたプロジェクト - 執筆時点で現在の 2.4.10 安定版リリースのような - は、そのサポートサイクルの寿命にわたって Base64 API の全量を得られます。
小さな驚き
知るとクラスがもっと面白くなる、いくつかの詳細:
- インスタンスなしの
Base64.encode(bytes)が動くのは、Base64.Defaultがコンパニオンオブジェクトに定義されているからです。コンパニオンそのものがデフォルト・スキームなので、シュガーの形と名前付きの形は文字通り同じオブジェクトです。 encodeToAppendable関数はビルダー形式です:目的の Appendable を返すので、文書化されたパターンは戻り値を無視して、あなたのビルダーを使い続けることです。- パディングは絶対にフル・グループを埋めません:base64 文字列はゼロ個、1個、2個の
=で終わり、パッドを数えれば、元の最終トリプルに何バイト残っていたか正確に分かります。 Pemは4つのプリセットで最も新しく、2.2 に合流しました。64 文字の折り返しは、隣に置かれている RFC 2045 のルールより古い PKI の慣習です。- JVM では標準ライブラリは意図的に
java.util.Base64に委任しません。2つの実装は別物で、これが全プラットフォームで挙動を同一に保ち、コストは、Kotlin チームが Java API がそれを許す未来のためにツリーに残し続けているコメントアウトされた最適化です。 Base64を安定化させた同じ 2.2 リリースは、Kotlin 1.9 以来実験的なkotlin.textの16進フォーマット・クラスHexFormatも安定化させたので、バイトレベルのテキストエンコーディングは標準ライブラリに落ち着いた住処を持つようになりました。
まとめと、次の行き先
Kotlin で Base64 を産むことは、短い意図的な選択のリストです:チャネルでプリセットを選び、パディングは意図的に決める、入力がテキストなら、文字コードは必ず明示にし、ペイロードが大きいなら 4/3 の増税を尊重する。それ以外はすべて - ファイル、バッファ、Appendable、ストリーム - 同じ4つのインスタンスの薄いラッパーです。もう1つの方向、そのパックされたテキストをバイトへ取り戻すこと - には独自の厳格さのルール、独自の失敗モード、独自の罠があり、姉妹サイトの関連記事が Kotlin での Base64 デコードを深く扱っています。
最終更新: 2026-10-09