🤖

JavaScript での バイナリ → base64 変換

2 min read

やりたいこと

// ArrayBuffer を base64 に変換したい
const arrayBuffer = // なんらかのバイナリ(画像・テキストなどなど)
const encodedData = convBase64(arrayBuffer)

しかし、convBase64(arrayBuffer)のような便利な関数をJSは提供してくれないため自分である程度考える必要がある。

色々調べてみる

ヒントになりそうな関数にbtoa関数がある

https://developer.mozilla.org/ja/docs/Web/API/btoa

Base64 でエンコードされた ASCII 文字列をバイナリ文字列から生成します。

なるほど。ただ、このバイナリ文字列とはなんだろう?

https://developer.mozilla.org/ja/docs/Web/API/DOMString/Binary

バイナリー文字列とは、この ASCII 部分集合と似た概念ですが、コードポイントを 127 までではなく、255 まで許可するものです

つまり、 バイナリ文字は1バイトで表現できる文字 であり
バイナリ文字列とは、バイナリ文字が連なったものということになる

JSの文字列はUTF-16によりエンコードされている。
以下のコードを見た方がわかりやすいだろう。

const notOK = "✓"
// ↓は 文字列:"✓"のコードポイントをNumberで出力したあと、16進数化しているコード
console.log(notOK.codePointAt(0).toString(16)); // => 2713
// 長さ > 1 バイト、 つまり、"✓"はバイナリ文字ではない

解決へのプロセスが見えてきた。整理する

  1. バイナリをバイナリ文字列化する
  2. バイナリ文字列を btoa 関数に与えて、エンコード結果を得る

1. バイナリをバイナリ文字列化する

MDNからの引用だが、勉強になったコード
(string から バイナリ文字列に変換するコード)

function toBinary(string) {
  // JSの文字列はUTF-16からなるので、16bitの箱を用意する
  const codeUnits = new Uint16Array(string.length);
  for (let i = 0; i < codeUnits.length; i++) {
    // 1文字ずつコードポイントを出力して、入れていく
    codeUnits[i] = string.charCodeAt(i);
  }
  // (...) 内がわかりにくいが、16bit 毎だったバイナリを 8bit 毎にわけて、それぞれを文字列に変換している
  // Uint8Array の 要素の範囲は 0..255 であるため変換後の文字が`バイナリ文字`であることが保証できる
  return String.fromCharCode(...new Uint8Array(codeUnits.buffer));
}

今回はバイナリ→バイナリ文字列なので以下のようなコードになる

function arrayBufferToBinaryString(arrayBuffer) {
  let binaryString = "";
  const bytes = new Uint8Array(arrayBuffer);
  const len = bytes.byteLength;
  for (let i = 0; i < len; i++) {
    binaryString += String.fromCharCode(bytes[i]);
  }
  return binaryString
}

ArrayBuffer をいじるには view オブジェクトを使う必要があったりと
少し取り回しがプリミティブな配列とは異なる。
ArrayBuffer の操作については以下が参考になる

https://ja.javascript.info/arraybuffer-binary-arrays

2. バイナリ文字列を btoa 関数に与えて、エンコード結果を得る

あとは簡単です。

function arrayBufferToBinaryString(arrayBuffer) {
  let binaryString = "";
  const bytes = new Uint8Array(arrayBuffer);
  const len = bytes.byteLength;
  for (let i = 0; i < len; i++) {
    binaryString += String.fromCharCode(bytes[i]);
  }
  return binaryString
}

const arrayBuffer = // なんらかのバイナリ(画像・テキストなどなど)
const binaryString = arrayBufferToBinaryString(arrayBuffer)
const encodedData = btoa(binaryString) // base64 にエンコードできた!

// 意外と、バイナリ → base64 変換も大変だな・・・。

所感

基礎的なところなんだけど、結構おざなりに仕事しちゃってるなぁ...
という自戒から記事にしてみました

GitHubで編集を提案

Discussion

ログインするとコメントできます