🪬

TypeScriptで文字列のdeflate&base64エンコード

2024/09/11に公開


TypeScript(JavaScript)でJSONを文字列エンコードしたくなったので実装した備忘メモです。

base64エンコード,デコード

実装

code
class Base64Codec {
  #encoder: TextEncoder | undefined;
  #decoder: TextDecoder | undefined;

  constructor(encode: boolean = false, decode: boolean = false) {
    if (encode === decode) { [encode, decode] = [true, true]; }
    if (encode) { this.#encoder = new TextEncoder; }
    if (decode) { this.#decoder = new TextDecoder; }
  }
  encode(raw: string): string {
    if (this.#encoder === undefined) { return ""; }
    return btoa(String.fromCodePoint(...(this.#encoder.encode(raw))));
  }
  decode(enc: string): string {
    if (this.#decoder === undefined) { return ""; }
    return this.#decoder.decode(Uint8Array.from([...atob(enc)].map(x => x.charCodeAt(0))));
  }
}

使い方

const test = "Lorem ipsum dolor sit amet, consectetur adipiscing elit";

const base64 = new Base64Codec();
console.log(base64.encode(test));
// "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdA=="
console.log(base64.decode(base64.encode(test)));
// "Lorem ipsum dolor sit amet, consectetur adipiscing elit"

簡単な解説

encode(raw: string): string {
  if (this.#encoder === undefined) { return ""; }
  return btoa(String.fromCodePoint(...(this.#encoder.encode(raw))));
}
  1. TextEncoderで文字列をutf8のUint8Arrayに変換
  2. Uint8Arrayの各要素をLatin-1文字に変換
  3. btoa関数でLatin-1文字列をbase64エンコード

decode(enc: string): string {
  if (this.#decoder === undefined) { return ""; }
  return this.#decoder.decode(Uint8Array.from([...atob(enc)].map(x => x.charCodeAt(0))));
}
  1. atob関数でbase64符号をLatin-1文字列にデコード
  2. Latin-1文字列の各文字を文字コードに変換
  3. Uint8Arrayを取得し、TextDecoderで元の文字列取得

deflate&base64エンコード,デコード

実装

(2024/09/13追記 残念ながらCompressionStreamは使い回せないようなので関数に変更しました)

code
async function encodeDeflateString(raw: string, url: boolean = true): Promise<string> {
  const enc = btoa(String.fromCodePoint(...(new Uint8Array(await new Response(new Blob([raw]).stream().pipeThrough(new CompressionStream("deflate-raw"))).arrayBuffer()))));
  return url ? enc.replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "") : enc;
}
async function decodeDeflateString(enc: string, url: boolean = true): Promise<string> {
  if (url) { enc = enc.replaceAll("-", "+").replaceAll("_", "/"); }
  return await new Response(new Blob([Uint8Array.from([...atob(enc)].map(x => x.charCodeAt(0)))]).stream().pipeThrough(new DecompressionStream("deflate-raw"))).text();
}

使い方

async function show(raw: string, url: boolean) {
  const enc = await encodeDeflateString(raw, url);
  const dec = await decodeDeflateString(enc, url);
  console.log(dec);
  console.log(enc);
}

const raw1 = "{foo:123,bar:'234',baz:[9,8,7,6,5],qux:false}";
show(raw1, true);
// "{foo:123,bar:'234',baz:[9,8,7,6,5],qux:false}"
// "q07Lz7cyNDLWSUosslI3MjZR10lKrLKKttSx0DHXMdMxjdUpLK2wSkvMKU6tBQA"

const raw2 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit";
show(raw2, false);
// "Lorem ipsum dolor sit amet, consectetur adipiscing elit"
// "BcGBCcAwCATAVX6AjtIlgnnKQ4xBzf69eyPp0KnrmLEiUWoMZz+w2EVr9k2MqaMy7Q9c6h8="

簡単な解説

async function encodeDeflateString(raw: string, url: boolean = true): Promise<string> {
  const enc = btoa(String.fromCodePoint(...(new Uint8Array(await new Response(new Blob([raw]).stream().pipeThrough(new CompressionStream("deflate-raw"))).arrayBuffer()))));
  return url ? enc.replaceAll("+", "-").replaceAll("/", "_").replaceAll("=", "") : enc;
}
  1. 文字列をblob streamに変換
  2. CompressionStream"deflate-raw"圧縮
  3. 得られたバイト列をUint8ArrayとしてLatin-1文字列に変換
  4. btoa関数でLatin-1文字列をbase64エンコードした文字列を取得
  5. urltrue(URLセーフ)の場合、結果文字列をURLセーフに加工

async function decodeDeflateString(enc: string, url: boolean = true): Promise<string> {
  if (url) { enc = enc.replaceAll("-", "+").replaceAll("_", "/"); }
  return await new Response(new Blob([Uint8Array.from([...atob(enc)].map(x => x.charCodeAt(0)))]).stream().pipeThrough(new DecompressionStream("deflate-raw"))).text();
}
  1. urltrue(URLセーフ)の場合、URLセーフから通常のBase64に加工
  2. atob関数でbase64符号をLatin-1文字列にデコード
  3. Latin-1文字列の各文字を文字コードに変換
  4. Uint8Arrayを取得しblob streamに変換
  5. DecompressionStream"deflate-raw"解凍した結果を文字列として取得

参考文献

Discussion