Haben Sie mit dem Base64-Format zu tun? Dann ist diese Website genau das Richtige für Sie! Nutzen Sie unser superpraktisches Online-Tool, um Ihre Daten zu kodieren oder zu dekodieren.

Base64-Kodierung in Ruby: Ein vollständiger Leitfaden

Ihre Daten haben ein Ziel, das ihr Aussehen nicht annimmt. Ein Bild, das in einem JSON-Dokument leben muss. Ein Token, das eine URL überqueren muss. Ein Anhang, der ein Protokoll überstehen muss, das für 7-Bit-Text entworfen wurde. Ein Geheimnis, das in eine Umgebungsvariable muss, ohne das Quoting zu zerstören. An jedem dieser Orte ist etwas zwischen hier und dort kurz davor, Ihre Binärdaten zu zerstören - und die Lösung hat einen Namen: Base64.

In Ruby wohnt der ganze Job in einem einzigen Modul, das mit der Sprache ausgeliefert wird. Drei Encoder, nichts zu installieren, und eine Ausgabe, die Sie bis auf das Zeichen vorhersagen können, bevor Sie den Code je laufen lassen. Diese Vorhersehbarkeit ist die Hälfte der Geschichte, die die meisten Guides überspringen, denn Kodierung ist der Ort, an dem Überraschungen ihre Gebühr kassieren: ein anhängendes Zeilenende schleicht sich in Ihr JSON, ein Zeilenumbruch, den Sie nicht verlangt haben, teilt ein Token, und eine einzige falsche Alphabetwahl ruiniert eine URL. Dieser Leitfaden geht alle drei Encoder durch, die Mathematik der Ausgabe und jeden Payload, den ein Ruby-Entwickler tatsächlich kodiert, damit die Überraschungen aufhören, Überraschungen zu sein.

Eine kurze Auffrischung, bevor wir loslegen: Base64 schreibt Daten drei Bytes zur Zeit um und gibt vier Zeichen aus einem 64-Symbol-Alphabet aus, mit ein oder zwei =-Zeichen als Padding, wenn sich die Eingabe nicht exakt durch drei teilen lässt - und deshalb ist die Ausgabe am Ende auch etwa ein Drittel größer als die Eingabe. Die Startseite dieser Site deckt das Format gründlich ab, also hält dieser Artikel das Format-Gespräch auf einen Atemzug und geht direkt an die Arbeit.

Welchen Encoder brauchen Sie?

Ruby gibt Ihnen drei Encoder, und die Wahl zwischen ihnen ist ein Drei-Fragen-Quiz: Darf die Ausgabe Zeilenumbrüche enthalten? Darf sie + oder / enthalten? Darf sie Padding enthalten? Hier ist die komplette Truppe:

Encoder Form der Ausgabe Zeilenumbrüche Padding Greifen Sie zu, wenn
Base64.strict_encode64(bin) eine Zeile, Standardalphabet nie immer vorhanden JSON, Tokens, APIs, Dateien - der sichere Standard
Base64.encode64(bin) mehrere Zeilen, Standardalphabet nach jeweils 60 Zeichen, plus ein anhängendes am Ende immer vorhanden E-Mail-Bodies und andere zeilenorientierte Text-Protokolle
Base64.urlsafe_encode64(bin, padding: true) eine Zeile, Bindestrich-Unterstrich-Alphabet nie Ihre Wahl, standardmäßig an alles, was in eine URL, ein Cookie oder einen Identifikator landet

Wenn Sie unter Zeitdruck entscheiden, ist die kurze Antwort: strict_encode64 als Standard, urlsafe_encode64, wenn das Ergebnis innerhalb einer URL reisen wird, und encode64 nur, wenn die empfangende Seite ein Text-Protokoll ist, das kurze Zeilen will. Alles darunter erklärt, warum das so ist, und wo jede Wahl still ihren Preis kostet.

strict_encode64: Das Arbeitstier

Base64.strict_encode64 ist der Encoder, den Sie in der großen Mehrheit Ihres Codes tatsächlich verwenden werden. Er produziert genau eine Zeile Ausgabe, immer mit dem richtigen Padding, aus dem Standardalphabet:

require "base64"
Base64.strict_encode64("hello world")
# => "aGVsbG8gd29ybGQ="
Base64.strict_encode64("s")
# => "cw=="

Und weil der Algorithmus deterministisch ist, können Sie die exakte Länge der Ausgabe aus der Eingabe vorhersagen - kein Raten, keine off-by-one-Bugs in Ihren Datenbankspalten. Die Tabelle unten ist die gesamte Arithmetik:

Eingabelänge Ausgabelänge Padding am Ende
3n Bytes (teilt sich exakt) 4n Zeichen keins
3n + 1 Bytes 4n + 4 Zeichen zwei =
3n + 2 Bytes 4n + 4 Zeichen ein =

Also werden aus 11 Bytes 16 Zeichen, aus 100 Bytes 136, und eine 1-Megabyte-Datei wird etwa 1,33 Megabyte Text. Das Drittel-Wachstum ist der Preis des Eintritts für jeden Base64-Payload, den Sie ausliefern, und es ist die Zahl, die Sie in der hinteren Tasche behalten sollten, wann immer eine Spalte, ein Cache oder ein API-Ratenlimit enger zu werden beginnt.

Base64.strict_encode64("123")
# => "MTIz"        3 Bytes rein, 4 Zeichen raus
Base64.strict_encode64("1234")
# => "MTIzNA=="   4 Bytes rein, 8 Zeichen raus, zwei Pad-Zeichen
Base64.strict_encode64("12345")
# => "MTIzNDU="   5 Bytes rein, 8 Zeichen raus, ein Pad-Zeichen

encode64: Der, der Zeilenumbrüche hinzufügt

Base64.encode64 ist der Klassiker, und er hat ein Verhalten, das schon so manchen Nachmittag beendet hat: Er bricht seine Ausgabe um. Alle 60 Zeichen beginnt es eine neue Zeile, und es endet immer mit einem anhängenden Zeilenumbruch:

Base64.encode64("hello world")
# => "aGVsbG8gd29ybGQ=\n"
Base64.encode64("*" * 46)
# => "KioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioqKioq\nKg==\n"

Der Umbruch ist kein Bug - er ist ein Feature, vererbt aus dem ursprünglichen Zuhause der Methode in der MIME-Welt, wo lange Zeilen eine Protokollverletzung waren. Rubys mail-Gem stützt sich bewusst darauf - sein Base64-Encoder trägt sogar einen Kommentar dahingehend, dass Rubys Zeilenumbruch die Ausgabe innerhalb der SMTP-Zeilenlängen-Grenzen hält. Wenn Sie E-Mail-Bodies kodieren, tut encode64 Ihnen einen Gefallen.

Aber in jedem anderen Kontext ist der Umbruch eine Steuer. Der häufigste Unfall ist ein JSON-Dokument, in dem ein Base64-Wert plötzlich über zwei Zeilen geht:

payload = { "logo" => Base64.encode64(File.binread("logo.png")) }
puts payload.to_json
# der logo-Wert trägt Zeilenumbrüche, die niemand bestellt hat

Und der kleinere Zwillingsbruder desselben Bugs ist das anhängende Zeilenende bei kurzen Strings: Base64.encode64("s") liefert "cw==\n" zurück, also scheitert ein Token, das Sie in eine URL einfügen oder mit einem erwarteten Wert vergleichen, aus Gründen, hinter denen Sie zwanzig Minuten herjagen. Die Kur ist ein strip - aber die bessere Kur ist strict_encode64, das nie ein einzelnes Zeichen hinzufügt, das Sie nicht verdient haben. Es gibt auch eine charmante Asymmetrie, die es zu wissen lohnt: Eine leere Eingabe produziert einen leeren String ohne anhängendes Zeilenende, also ist Base64.encode64("") einfach "".

urlsafe_encode64: Das URL-sichere Alphabet

Zwei Zeichen im Standardalphabet machen überall Schwierigkeiten, wo ein URL-Parser zuschaut: + (ein Leerzeichen in Query-Strings) und / (ein Pfadtrenner). RFC 4648 löste das mit einem Tausch - - nimmt den Platz von + ein, _ den Platz von / - und Ruby implementiert es in Base64.urlsafe_encode64:

Base64.urlsafe_encode64("\xfb\xef\xbe".b)
# => "----"
Base64.urlsafe_encode64("\xff\xff\xff".b)
# => "____"

Diese zwei Beispiele sind das Alphabet in Aktion: dieselben Bytes, die der Standard-Encoder als ++++ oder //// rendert, kommen als ---- und ____ heraus, Zeichen, die URLs, Pfade, Dateinamen und Formularfelder ohne jegliches Percent-Encoding überleben. Die Ausgabe ist eine Zeile, genau wie bei strict_encode64.

Die einzige Option der Methode ist das padding:-Keyword, hinzugefügt in Ruby 2.3, und es ist die, die man kennen sollte. Die JSON-Web-Token-Spezifikation verlangt base64url ohne Padding, und das tun es auch viele andere Token-Schemata:

Base64.urlsafe_encode64("*")
# => "Kg=="
Base64.urlsafe_encode64("*", padding: false)
# => "Kg"

Mit ausgeschaltetem Padding verschiebt sich die Längen-Mathematik: 3n + 1 Bytes liefern jetzt 4n + 2 Zeichen, und 3n + 2 Bytes liefern 4n + 3. Die Decoder-Seite kommt zurecht - Rubys urlsafe_decode64 füllt das fehlende Padding selbst ein - also ist ungepaddete Ausgabe sicher auszugeben, aber gepaddete Ausgabe ist der freundlichere Standard, wenn die andere Seite ein strikter RFC-2045-Leser ist. Eine Warnung: Schalten Sie Padding nur dann aus, wenn eine Spezifikation es verlangt. Es spart ein oder zwei Zeichen und kauft Ihnen eine Klasse von Decoder-Beschwerden ein.

Was Ruby tatsächlich kodiert: Strings sind Bytes

Bevor es zu den Anwendungsfällen geht, eine Ruby-spezifische Tatsache, die alles formt: Ein Ruby-String ist eine Folge von Bytes mit einem Kodierungs-Tag, und die Encoder sehen sich nur die Bytes an. Das Tag sagt Ruby, wie der String angezeigt und verglichen wird; es ändert nicht, was kodiert wird:

require "base64"
s = "h\u{e9}llo"
puts s.encoding
# => UTF-8
puts s.bytes.length
# => 6   das betonte e ist zwei Bytes
Base64.strict_encode64(s)
# => "aMOpbGxv"

Das ist die Falle hinter "Warum ist meine Ausgabe länger als erwartet": Der String, den Sie getippt haben, ist in Zeichen meistens kürzer als in Bytes, und Base64 berechnet pro Byte. Die umgekehrte Richtung ist genauso still - ein ungültiger UTF-8-String wird ohne jede Beschwerde kodiert, weil der Encoder nichts zu validieren hat:

broken = "h\u{e9}llo".b.force_encoding("UTF-8")
broken.setbyte(1, 0xFF)
puts broken.valid_encoding?
# => false
Base64.strict_encode64(broken)
# => irgendein base64, kein Fehler, Bytes sind Bytes

Für echtes Binär überspringen Sie die Text-Maschinerie ganz und bauen Sie die Bytes mit pack oder lesen Sie sie mit File.binread. Ein befriedigendes Beispiel ist die PNG-Signatur - die acht Bytes, mit denen jede PNG-Datei auf der Erde beginnt:

png_magic = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A].pack("C*")
Base64.strict_encode64(png_magic)
# => "iVBORw0KGgo="

JWTs: Daten signieren, die auch lesbar sind

JSON Web Tokens sind der bekannteste Verbraucher von Rubys URL-sicherem Encoder. Ein Token besteht aus drei base64url-Segmenten, verbunden durch Punkte - Header, Payload, Signatur - und die Spezifikation ist eindeutig: Das Alphabet muss das URL-sichere sein, und das Padding muss aus sein. Das jwt-Gem übernimmt all das:

# Im Gemfile: gem "jwt"
require "jwt"
token = JWT.encode(
  { sub: "1234567890", name: "Alice", exp: Time.now.to_i + 3600 },
  "my-secret-key",
  "HS256"
)
puts token
# => eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIi...
payload, header = JWT.decode(token, "my-secret-key", true, algorithm: "HS256")
puts header
# => {"alg"=>"HS256"}

Sie können auch zusehen, wie die Base64-Ebene im Inneren des Tokens ihre Arbeit tut, denn die Segmente sind einfach base64url von JSON:

require "base64"
require "json"
payload_json = JSON.generate({ "sub" => "1234567890", "name" => "Alice" })
segment = Base64.urlsafe_encode64(payload_json, padding: false)
puts segment
# => eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkFsaWNlIn0

Zwei Regeln gehören zu diesem Anwendungsfall. Rollen Sie nie Ihren eigenen JWT von Hand in Produktion - die Signatur ist es, was ein Token zu mehr als einem Geständnis macht - und wenn Sie mit dem Gem dekodieren, nageln Sie den Algorithmus im Options-Hash fest wie oben gezeigt, damit der eigene Header des Tokens die Verifizierungsmethode nicht für Sie aussuchen kann.

HTTP Basic Auth: Den Header bauen

Der älteste Weg, in HTTP zu sagen "wer bin ich", ist immer noch der einfachste: Die Zugangsdaten in Base64, sie hinter das Wort Basic stellen und den Header senden. In Ruby ist das Bauen eine Zeile:

require "base64"
credentials = Base64.strict_encode64("alice:s3cr3t!")
puts "Basic #{credentials}"
# => Basic YWxpY2U6czNjcjN0IQ==

Die Standardbibliothek von Ruby erledigt genau das für Sie in Net::HTTP, und ruft die Kern-pack-Vorlage direkt auf - ["user:pass"].pack("m0") ist, was basic_auth unter der Haube auf sich reduziert:

require "net/http"
request = Net::HTTP::Get.new("https://example.org/api")
request.basic_auth("alice", "s3cr3t!")
puts request["Authorization"]
# => Basic YWxpY2U6czNjcjN0IQ==

Und der Sicherheitshinweis, einmal ausgesprochen, damit er aktenkundig ist: Base64 ist ein Übersetzer, kein Schloss. Die Zugangsdaten in einem Basic-Auth-Header sind für jeden lesbar, der das Paket lesen kann. Dieser Header ist nur über HTTPS akzeptabel, wo der Transport den eigentlichen Schutz leistet.

Data-URIs: Inline-Bilder und Schriftarten

Ein Data-URI ist die Antwort des Webs auf "Ich will dieses Bild ohne eine separate Datei": ein Medientyp, das Wort base64, ein Komma und die Bytes. So liefern Single-File-HTML-Demos ihre Logos aus, so verstecken sich Favicons in CSS, und so kann ein generiertes Bild komplett in einem Template-String leben:

require "base64"
png = File.binread("logo.png")
data_uri = "data:image/png;base64,#{Base64.strict_encode64(png)}"
css = "background-image: url(#{data_uri});"
puts css.length
# => ihr Stylesheet, minus eine HTTP-Anfrage

Verwenden Sie hier strict_encode64 - der Payload ist eine einzige saubere Zeile, kein Umbruch, kein Newline. Und achten Sie auf die Größe: Das Bild, das Sie inline einbetten, wächst um etwa ein Drittel, also glänzen Data-URIs bei kleinen Assets (Favicons, Logos, Icon-Schriftarten) und blähen große auf. Ein zwei-Megabyte-Hero-Foto wird 2,7 Megabyte Ihres HTML-Dokuments, und Ihre Nutzer werden es bei ihrem ersten 4G-Scroll fühlen.

E-Mail: Woher Base64 kommt

Jeder andere Anwendungsfall in diesem Artikel ist ein Nachfahre von diesem. SMTP wurde in den 1980er-Jahren für kurze Zeilen aus 7-Bit-Text entworfen, was so viel heißt wie: Es konnte kein JPEG tragen. Die Lösung - Privacy-Enhanced Mail, dann MIME 1993 - bestand darin, Binärdaten mit einem 64-Symbol-Alphabet als Text umzuschreiben, und das ist exakt das Format, das Sie heute verwenden. Die Narben sind in Rubys Ausgabe immer noch sichtbar: encode64 bricht bei 60 Zeichen um - eine Breite, die keinem bestimmten Protokoll dient, wie Sie später sehen werden, aber kurz genug, um E-Mail höflich zu halten.

In der Praxis werden Sie das mail-Gem die MIME-Arbeit tun lassen. Hängen Sie eine Binärdatei an, und das Gem wählt den Base64-Encoder, bricht die Zeilen um und schreibt die Header:

# Im Gemfile: gem "mail"
require "mail"
message = Mail.new do |m|
  m.from = "dev@example.org"
  m.to = "ops@example.org"
  m.subject = "Binary report"
  m.add_file("report.bin")
end
puts message.encoded
# der Anhang trägt Content-Transfer-Encoding: base64

Nicht-ASCII-Text in Headern bekommt dieselbe Behandlung in einem leicht anderen Kostüm: RFC-2047-kodierte Wörter, die Base64 in einem Zeichensatz-Tag zwischen Fragezeichen einpacken, wie =?UTF-8?B?w7wgc2VjcmV0cw==?=. Wenn Sie solche Wörter je von Hand bauen oder parsen, ist das Base64 drinnen die gewöhnliche Art, dekodiert mit decode64 und anschließend neu getaggt mit dem Zeichensatz, den das Wort erklärt.

PEM-Panzerung für Schlüssel und Zertifikate

Schlüssel und Zertifikate tragen PEM-Panzerung, und die Panzerung ist Base64 mit Rahmen: eine BEGIN-Zeile, die kodierten Bytes in Zeilen von 64 Zeichen und eine END-Zeile. Wenn Sie je eine PEM-Datei aus rohen DER-Bytes produzieren müssen, ist der Aufbau ein zweistufiges Einpacken:

require "base64"
der_bytes = File.binread("server.der")
body_lines = Base64.strict_encode64(der_bytes).scan(/.{1,64}/)
pem = (["-----BEGIN PRIVATE KEY-----"] + body_lines +
  ["-----END PRIVATE KEY-----"]).join("\n") + "\n"
File.write("server.key", pem)

Zwei Hinweise. Erstens werden Sie das fast nie brauchen, denn das openssl-Gem schreibt PEM für Sie (key.to_pem), und das Label zwischen den BEGIN- und END-Zeilen muss zu dem Inhalt passen - falsche Labels produzieren eine Datei, die jedes Tool im Internet ablehnt. Zweitens ist die Zeilenlänge hier 64, die klassische PEM-Breite; Rubys encode64 bricht stattdessen bei 60 um, und jeder anständige PEM-Parser ignoriert Zeilenlängen vollständig, also dekodiert jede Breite problemlos.

Dateien: Die .b64-Konvention

Das häufigste Dateiformat in der Base64-Welt ist eine gewöhnliche Textdatei mit einer .b64 (oder .base64)-Endung, die einen einzigen kodierten Payload enthält - denken Sie daran wie an "die Datei, aber sicher überall einfügbar". Eine davon aus Ruby zu produzieren ist ein Einzeiler:

require "base64"
File.write("payload.b64", Base64.strict_encode64(File.binread("payload.bin")))
puts File.size("payload.b64")
# => ungefähr 1,33-mal die Originalgröße

Verwenden Sie strict_encode64, damit die Datei eine einzige saubere Zeile enthält - die Konvention, die die meisten Dekodier-Tools (und Rubys strenger Decoder) erwarten. Sie zurückzulesen ist das Spiegelbild: lesen, dekodieren und die Bytes im Binärmodus schreiben, damit nichts sie auf dem Weg raus verändert:

encoded = File.read("payload.b64")
bytes = Base64.strict_decode64(encoded)
File.binwrite("restored.bin", bytes)

Wenn Ihre .b64-Dateien von Tools kommen, die Zeilen umbrechen - manche base64-CLI-Varianten tun das - streichen Sie die Zeilenumbrüche vor einem strengen Dekodieren, oder verwenden Sie den nachsichtigen Decoder, der sie gratis überspringt.

Konfigurationsdateien, Umgebungsvariablen und Datenbanken

Wo immer Binärdaten in einem Textdokument leben müssen, ist Base64 die Brücke. Das Muster wiederholt sich an drei Orten mit kleinen Variationen.

Umgebungsvariablen und .env-Dateien können keine rohen Bytes halten, also werden die Bytes kodiert, bevor sie den Computer verlassen, der sie hat:

require "base64"
# irgendwo, wo Sie die App provisionieren
ENV["APP_LOGO"] = Base64.strict_encode64(File.binread("logo.png"))
# irgendwo, wo die App startet
b64 = ENV.fetch("APP_LOGO")
File.binwrite("logo.png", Base64.decode64(b64))

YAML hat einen nativen Binär-Typ, und Psych übernimmt das Base64 für Sie - ein BINARY-String, der nach YAML gedumpt wird, kommt als !binary-Skalar heraus und lädt byteidentisch zurück:

require "yaml"
yaml_text = YAML.dump({ "logo" => File.binread("logo.png") })
puts yaml_text.lines.first(2)
# => "---"
# => "logo: !binary |-"
data = YAML.load(yaml_text)
puts data["logo"].encoding
# => ASCII-8BIT

In Datenbanken ist die Frage die vom Speichertyp, nicht die von der Kodierung. Wenn Ihre Datenbank eine echte Binärspalte hat - BLOB, BYTEA, VARBINARY - verwenden Sie sie, und lassen Sie den Driver die Bytes tragen. Base64-in-einer-TEXT-Spalte ist das Muster für den Fall, dass die Schicht darunter nur Strings spricht: manche Dokumenten-Store, JSON-artige APIs oder ein Legacy-Schema, das Sie nicht ändern können. Der Preis ist die ein-Drittel-Größensteuer auf der Spalte, und die Disziplin, beim Hineinkommen zu kodieren und beim Hinauskommen zu dekodieren, an jeder Grenze, ohne Ausnahme.

Prüfsummen, die als Text reisen

Hashes sind binär, aber Prüfsummen reisen meist als Text: Datei-Integritätslisten, Cache-Keys, Fingerprints, Log-Zeilen. Jede Digest-Klasse von Ruby hat eine base64digest-Methode, die die Kodierung in einem Aufruf erledigt:

require "digest"
Digest::SHA256.base64digest("hello")
# => "LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ="

Die Ausgabe ist gepaddetes Standard-Base64 - dasselbe, was Sie aus Base64.strict_encode64(Digest::SHA256.digest("hello")) bekommen würden - also ist sie sicher zu speichern, zu vergleichen und einzufügen. Die einzige Entscheidung ist Konsistenz: Eine Prüfsummenliste, die mit Base64 erzeugt wurde, muss gegen Base64-Ausgabe geprüft werden, und die hex- und Base64-Darstellungen desselben Hashes sind unterschiedliche Strings, also wählen Sie eine und bleiben Sie dabei.

Große Dinge in kleinen Chunks kodieren

Wie die Decoder sind die Encoder pufferbasiert: Sie lesen die gesamte Eingabe und geben die gesamte Ausgabe aus. Es gibt keinen Streaming-Encoder in der Standardbibliothek, also lautet der Plan für große Payloads: Speicher, und die Mathematik hat eine angenehme Symmetrie. Kodierung vergrößert Ihre Daten um ein Drittel, also ist die Ausgabe - nicht die Eingabe - Ihre größte Allokation, und für eine 1-Gigabyte-Datei sollten Sie mit etwa 1,33 Gigabyte Text davor rechnen.

Wenn das zu viel ist, um es auf einmal zu halten, können Sie in Chunks kodieren, denn das Base64-Alphabet ist auf Drei-Byte-Grenzen selbstsynchronisierend: Kodieren Sie jedes 3-Byte-Slice unabhängig, und die Verkettung ist identisch mit dem Kodieren des Ganzen:

require "base64"
require "securerandom"
bin = SecureRandom.random_bytes(10_001)
whole = Base64.strict_encode64(bin)
chunked = bin.scan(/.{1,3}/m).map { |slice| Base64.strict_encode64(slice) }.join
puts chunked == whole
# => true

Derselbe Trick gibt Ihnen einen selbstgebauten Zeilenwrapper, der exakt encode64 entspricht: 45 Bytes kodieren immer zu genau 60 Zeichen, also reproduzieren Sie die klassische MIME-Ausgabe, indem Sie die Eingabe bei 45 Bytes aufschneiden und die Teile mit Zeilenumbrüchen verbinden, eine Zeile zur Zeit, sodass nie mehr als ein Slice im Speicher ist:

def wrap_like_encode64(bin)
  lines = bin.scan(/.{1,45}/m).map { |slice| Base64.strict_encode64(slice) }
  lines.join("\n") + "\n"
end
bin = SecureRandom.random_bytes(10_001)
puts wrap_like_encode64(bin) == Base64.encode64(bin)
# => true

Von der Kommandozeile

Kodieren braucht auch keine Skriptdatei. Die Einzeiler-Form liest eine Datei und schreibt ihr Base64 nach stdout:

ruby -rbase64 -e 'print Base64.strict_encode64(File.binread(ARGV[0]))' payload.bin > payload.b64

Und die Pipe-Form liest stdin, und so würden Sie einen Bytes-Stream aus jedem anderen Kommando verpacken:

some_command | ruby -rbase64 -e 'print Base64.strict_encode64(STDIN.read)'

Behalten Sie in beiden print bei - ein versehentliches puts würde einen Zeilenumbruch an Ihr Base64 hängen, und bei strict_encode64-Ausgabe macht das aus einem sauberen Token ein kaputtes. Dieselbe Faustregel wie auf der Decode-Seite: Wenn der nächste Verbraucher Ihrer Ausgabe streng ist, darf nichts außer dem Base64 selbst mitreisen.

Die Fallen, die Ruby-Entwickler Extra-Bytes kosten

  • Das anhängende Zeilenende in JSON. Base64.encode64 beendet jedes nicht leere Ergebnis mit einem Zeilenumbruch, also kommt ein Wert, der ein sauberes Token sein sollte, in Ihrem JSON mit einem Überraschungs-\n am Ende an. Verwenden Sie strict_encode64 für alles, was in einer einzelnen Zeile gespeichert, verglichen oder gesendet werden soll.
  • Der 60-Zeichen-Umbruch in Tokens und URLs. Dieselbe Methode bricht lange Ausgabe in mehrere Zeilen um. Eine umgebrochene Zeichenfolge in einer URL sind zwei URLs, und ein umgebrochenes Token ist ein kaputtes. Wieder: strict_encode64, oder strip/delete für die Zeilenumbrüche, wenn Sie an encode64-Ausgabe festgehalten werden.
  • Plus und Slash in URLs. Standard-Base64 in einem Query-String bedeutet Percent-Encoding von %2B, %2F und %3D auf dem Weg raus und die Hoffnung, dass die andere Seite sie dekodiert. urlsafe_encode64 entfernt das Problem an der Quelle.
  • Padding am falschen Ort. JWTs und andere Token-Schemata wollen Padding aus, MIME-Leser kommen vielleicht nicht mit fehlendem Padding zurecht. Geben Sie padding: false nur dort aus, wo eine Spezifikation es verlangt, und wissen Sie, auf welcher Seite dieses Zauns jeder Ihrer Verbraucher sitzt.
  • Zeichen sind keine Bytes. Eine fünf Zeichen lange Zeichenfolge mit einem akzentuierten Buchstaben ist sechs Bytes in UTF-8, und die Ausgabelängen-Mathematik läuft auf Bytes. Wenn das kodierte Ergebnis "zu lang" ist, zählen Sie Bytes, nicht Zeichen.
  • Die ein-Drittel-Steuer im Schema-Design. Ein 16-KB-BLOB wird ein Base64-String von rund 22 KB in einer TEXT-Spalte. Dimensionieren Sie Ihre Spalten, Caches und API-Payloads für die kodierte Form, nicht für die binäre Form.
  • Zwei Alphabete, zwei unterschiedliche Strings. Dieselben Bytes kodieren sich im Standard- und im URL-sicheren Alphabet unterschiedlich, also ist ein kodierter Wert nur gegen einen anderen Wert aus demselben Alphabet vergleichbar. Vergleichen oder vermischen Sie sie nie.
  • Base64 ist kein Schloss. Ein Geheimnis zu kodieren macht es nicht geheim. Jeder, der den String hat, hat Ihre Daten; Base64 steuert nur, wie die Bytes aussehen, nicht, wer sie lesen kann.

Gewohnheiten, die Bytes und Bugs sparen

  • Machen Sie strict_encode64 zu Ihrem Standard. Wechseln Sie zu urlsafe_encode64, sobald die Ausgabe in einer URL, einem Cookie oder einem Identifikator leben wird, und zu encode64 nur, wenn das Ziel ein zeilenorientiertes Text-Protokoll wie E-Mail ist.
  • Halten Sie das Alphabet zwischen Encoder und Decoder auf beiden Enden der Leitung konsistent. Der mit Abstand häufigste "Base64 kaputt"-Bug ist ein Standard-Alphabet-Produzent, der auf einen URL-sicheren Verbraucher trifft, oder umgekehrt.
  • Füttern Sie die Encoder mit Bytes, die Sie wirklich kodieren wollen: File.binread für Dateien, pack für konstruiertes Binär und ein UTF-8-String, wenn der String die Daten ist. Der Encoder wird Ihre Wahl nicht infrage stellen - er zählt nur Bytes.
  • Legen Sie ein Budget für das Wachstum an. Wann immer ein Base64-String eine Grenze zu einem Container mit Größe überquert, multiplizieren Sie mit 4/3 und geben Sie ein wenig Spielraum für das Padding dazu.
  • Verwenden Sie Base64 für Portabilität, nie für Geheimhaltung. Wenn das Ziel ist, die Daten privat zu halten, ist das Tool die Verschlüsselung, und Base64 ist nur das, was Sie danach mit dem Kryptotext tun.

Wie Base64 ein Gem wurde

Den Großteil seines Lebens war das Base64-Modul einfach eine Datei in der Standardbibliothek, wie viele der ältesten Helfer von Ruby. Die strict- und URL-sicheren Methoden kamen zum ursprünglichen Paar während der 1.9-Entwicklungsreihe dazu - die gesamte base64-Bibliothek, mit allen vier Methoden, wurde im September 2008 in den Trunk aufgenommen und erschien erstmals in 1.9.1 (2009), und das padding:-Keyword kam 2015 mit Ruby 2.3. Bis dahin hatte sich alles an der API, die Sie heute sehen, eingependelt - der Rest der Geschichte handelt davon, wie das Modul ausgeliefert wird.

2020, mit Ruby 3.0, begann das Core-Team, Standardbibliotheken in eigene Gems zu extrahieren, und base64 wurde eines davon: Version 0.1.0, betreut im ruby/base64-Repository von den Core-Beitragenden. Es erschien als Default-Gem - mit Ruby verteilt und immer verfügbar, also funktionierte require "base64" ohne jede Zeremonie weiter. Version 0.2.0 folgte 2023 mit Ruby 3.3 und fügte die Base64::VERSION-Konstante und einen deutlich reicheren Dokumentations-Satz hinzu.

Dann zeichnete Ruby 3.4 im Dezember 2024 die Linie neu: base64 wechselte von der Default-Gem-Liste auf die Bundled-Gem-Liste, dasselbe Regal wie csv und drb. Bundled Gems werden immer noch mit der Sprache ausgeliefert, aber Bundler-basierte Projekte sollen sie deklarieren, also fügen Sie bei Ruby 3.4 oder neuer und einer Bundler-geführten App gem "base64" zu Ihrem Gemfile hinzu (oder führen Sie gem install base64 aus), und Sie sind abgedeckt. Ruby 4.0 im Jahr 2025 brachte Version 0.3.0, mit RBS-Typsignaturen, damit statische Checker das Modul richtig sehen können.

Während der ganzen Reise blieb die Implementierung das, was sie immer war: ein paar Dutzend Zeilen reines Ruby um die Kern-pack- und unpack-Vorlagen. Keine C-Extension, keine Abhängigkeiten und - mit einer Download-Zahl im Bereich von Hunderten Millionen auf rubygems.org - eines der am meisten installierten Gems der Plattform.

Schöne Ruby-Fakten

  • Das gesamte Modul, Encoder inklusive, ist kurz genug, um es in einer Kaffeepause zu lesen. encode64 ist buchstäblich [bin].pack("m"), strict_encode64 ist [bin].pack("m0"), und urlsafe_encode64 ist der strenge Encoder mit einem Zwei-Buchstaben-Tausch obendrauf, minus das Padding, wenn Sie es verlangen.
  • Der 60-Zeichen-Umbruch von encode64 passt weder zu MIMEs 76-Zeichen-Maximum noch zu PEMs klassischem 64. Es ist einfach das, was die m-Pack-Vorlage immer getan hat, und der Base64-Encoder des mail-Gems kommentiert das zustimmend: Rubys automatischer Zeilenumbruch hält die Ausgabe innerhalb der SMTP-Grenzen.
  • Rubys Net::HTTP macht sich für Basic Auth nicht die Mühe mit dem Base64-Modul - es ruft die pack-Vorlage direkt auf, was eine schöne Erinnerung ist, dass das Modul eine Komfortschicht über dem Core ist, nicht umgekehrt.
  • Jede Digest-Klasse trägt eine base64digest-Methode, also ist Digest::SHA256.base64digest ein Bürger erster Klasse neben hexdigest - Prüfsummen in Text ohne zweiten Aufruf.
  • Der !binary-Tag von YAML ist Base64 in Verkleidung. Psych erledigt die Kodierung im Moment, in dem Sie einen BINARY-String dumpen, deshalb sehen Konfigurationsdateien voller Binärdaten so aus, wie sie aussehen.
  • Das Modul, das Sie verwenden, war nicht immer das, an das Sie denken. Altes Ruby hatte b64encode (Umbruch bei einer gewählten Breite) und decode_b (RFC-2047-Header-Dekodierung); beide verschwanden in der 1.9-Linie, also stirbt jeder vor-2010-Code, den Sie erben und der sie aufruft, mit einem NoMethodError.
  • Die Video-IDs von YouTube sind base64url ohne Padding - elf Zeichen, kein Plus, kein Slash, kein Gleichheitszeichen - genau die Art kurzer, URL-sicherer Identifikator, für die das URL-sichere Alphabet entworfen wurde.

Die Kehrseite

Sie haben jetzt das komplette Kodierungsbild: ein Standard-Arbeitstier, das Sie nie überrascht, ein Klassiker, der die Zeilen umbricht, wo die Protokolle es verlangen, ein URL-sicheres Alphabet mit einem Padding-Schalter und die Byte-Ebenen-Regeln, die genau entscheiden, wie Ihre Ausgabe aussehen wird. Die umgekehrte Richtung - einen Base64-String auseinanderzunehmen, zwischen den drei Decodern von Ruby zu wählen und die resultierenden Bytes in etwas Verwendbares zu verwandeln - hat ihre eigenen stillen Fallen, beginnend mit einem Decoder, der nie Nein sagt. Diese andere Seite der Straße wird in dem Artikel über Base64-Dekodierung in Ruby, verlinkt unten, in derselben Tiefe abgedeckt.

Zuletzt aktualisiert: 2026-09-08

Verwandter Artikel: Base64-Dekodierung in Ruby: Ein vollständiger Leitfaden