uint8配列をbase64エンコード文字列に変換する方法は?


90

webSocket通信を取得し、base64でエンコードされた文字列を受信し、それをuint8に変換して作業しますが、送信する必要があります。uint8配列を取得し、base64文字列に変換して送信できるようにします。どうすればこの変換を行うことができますか?



「ArrayBufferからbase64でエンコードされた文字列」という質問には、すべての文字を処理するより優れたソリューションが含まれています。stackoverflow.com/questions/9267899/...
スティーブHanov

回答:


16

すでに提案されているすべてのソリューションには深刻な問題があります。大きな配列で機能しないソリューション、間違った出力を提供するソリューション、中間文字列にマルチバイト文字が含まれている場合にbtoa呼び出しでエラーをスローするソリューション、必要以上のメモリを消費するソリューションがあります。

そこで、入力に関係なく機能する直接変換関数を実装しました。それは私のマシンで毎秒約500万バイトを変換します。

https://gist.github.com/enepomnyaschih/72c423f727d395eeaa09697058238727


base64abcを文字列の配列として使用する方が、単に文字列にするよりも高速ですか?"ABCDEFG..."
GarrGodfrey20年

163

データにマルチバイトシーケンス(プレーンASCIIシーケンスではない)が含まれている可能性があり、ブラウザにTextDecoderがある場合は、それを使用してデータをデコードする必要があります(TextDecoderに必要なエンコーディングを指定します)。

var u8 = new Uint8Array([65, 66, 67, 68]);
var decoder = new TextDecoder('utf8');
var b64encoded = btoa(decoder.decode(u8));

TextDecoderを備えていないブラウザー(現在はIEとEdgeのみ)をサポートする必要がある場合、最良のオプションはTextDecoderポリフィルを使用することです

データにプレーンASCII(マルチバイトUnicode / UTF-8ではない)が含まれている場合は、それを使用する簡単な代替手段があり、String.fromCharCodeかなり普遍的にサポートされているはずです。

var ascii = new Uint8Array([65, 66, 67, 68]);
var b64encoded = btoa(String.fromCharCode.apply(null, ascii));

そして、base64文字列をデコードしてUint8Arrayに戻します。

var u8_2 = new Uint8Array(atob(b64encoded).split("").map(function(c) {
    return c.charCodeAt(0); }));

非常に大きな配列バッファーがある場合、適用が失敗し、バッファーをチャンク化する必要がある場合があります(@RohitSengarによって投稿されたものに基づく)。繰り返しますが、これは、バッファにマルチバイト以外のASCII文字のみが含まれている場合にのみ正しいことに注意してください。

function Uint8ToString(u8a){
  var CHUNK_SZ = 0x8000;
  var c = [];
  for (var i=0; i < u8a.length; i+=CHUNK_SZ) {
    c.push(String.fromCharCode.apply(null, u8a.subarray(i, i+CHUNK_SZ)));
  }
  return c.join("");
}
// Usage
var u8 = new Uint8Array([65, 66, 67, 68]);
var b64encoded = btoa(Uint8ToString(u8));

4
これはFirefoxで機能していますが、Chromeは「UncaughtRangeError:Maximum call stack sizeexceeded」(btoaを実行)でチョークします。
Michael Paulukonis 2014

3
@MichaelPaulukonis私の推測では、スタックサイズを超えているのは実際にはString.fromCharCode.applyです。非常に大きなUint8Arrayがある場合は、applyを使用する代わりに、文字列を繰り返し作成する必要があります。apply()呼び出しは、配列のすべての要素をパラメーターとしてfromCharCodeに渡すため、配列の長さが128000バイトの場合、スタックを破壊する可能性のある128000パラメーターを使用して関数呼び出しを行おうとします。
カナカ2014年

4
ありがとう。必要なのはbtoa(String.fromCharCode.apply(null, myArray))
グレンリトル

29
バイト配列が有効なUnicodeでない場合、これは機能しません。
melab 2017年

11
base64文字列またはにマルチバイト文字はありませんUint8Array。128..255の範囲のバイトがある場合、テキストデコーダーが誤ってそれらをユニコード文字に変換し、base64コンバーターが機能しなくなるTextDecoderため、ここで使用するのは絶対に間違ってUint8Arrayいます。
riv 2018

26

非常にシンプルなソリューションとJavaScriptのテスト!

ToBase64 = function (u8) {
    return btoa(String.fromCharCode.apply(null, u8));
}

FromBase64 = function (str) {
    return atob(str).split('').map(function (c) { return c.charCodeAt(0); });
}

var u8 = new Uint8Array(256);
for (var i = 0; i < 256; i++)
    u8[i] = i;

var b64 = ToBase64(u8);
console.debug(b64);
console.debug(FromBase64(b64));

4
最もクリーンなソリューション!
realappie 2017年

完璧な解決策
Haris urRehman19年

2
大きなデータ(画像など)では失敗しますRangeError: Maximum call stack size exceeded
MaximKhokhryakov20年

20

Node.jsを使用している場合は、このコードを使用してUint8Arrayをbase64に変換できます

var b64 = Buffer.from(u8).toString('base64');

4
これは、パフォーマンスの観点から、上記の手巻き機能よりも優れた答えです。
BenLiyanage20年

2
驚くばかり!ありがとう。史上最高の答え
アラン

18
function Uint8ToBase64(u8Arr){
  var CHUNK_SIZE = 0x8000; //arbitrary number
  var index = 0;
  var length = u8Arr.length;
  var result = '';
  var slice;
  while (index < length) {
    slice = u8Arr.subarray(index, Math.min(index + CHUNK_SIZE, length)); 
    result += String.fromCharCode.apply(null, slice);
    index += CHUNK_SIZE;
  }
  return btoa(result);
}

非常に大きなUint8Arrayがある場合は、この関数を使用できます。これはJavascript用であり、FileReaderreadAsArrayBufferの場合に役立ちます。


2
興味深いことに、Chromeでは、これを300kb以上のバッファーで計時しましたが、バイトごとに実行するよりも少し遅くなるように、チャンクで実行することがわかりました。これは私を驚かせた。
マット

@マット面白い。その間に、Chromeがこの変換を検出し、特定の最適化が行われ、データをチャンク化すると効率が低下する可能性があります。
kanaka 2017

2
これは安全ではありませんね。チャンクの境界がマルチバイトのUTF8エンコード文字を通過する場合、fromCharCode()は境界の両側のバイトから適切な文字を作成できませんか?
イェンス

2
@JensString.fromCharCode.apply()メソッドはUTF-8を再現できません:UTF-8文字の長さは1バイトから4バイトまで変化する可能性がありますがString.fromCharCode.apply()、UInt8のセグメントでUInt8Arrayを検査するため、各文字が正確に1バイトの長さであり、隣接する文字から独立していると誤って想定しますもの。入力UInt8Arrayでエンコードされた文字がすべてASCII(シングルバイト)範囲にある場合、偶然に機能しますが、完全なUTF-8を再現することはできません。そのためには、TextDecoderまたは同様のアルゴリズムが必要です。
ジェイミーバーチ

1
@Jensは、バイナリデータ配列内のどのマルチバイトUTF8エンコード文字ですか?ここではUnicode文字列を扱っていませんが、utf-8コードポイントとして扱われるべきではない任意のバイナリデータを扱っています。
riv 2018

0

これに対するJS関数は次のとおりです。

ChromeはpushManager.subscribeのapplicationServerKeyの値としてbase64でエンコードされた文字列をまだ受け入れないため、この関数が必要です https://bugs.chromium.org/p/chromium/issues/detail?id=802280

function urlBase64ToUint8Array(base64String) {
  var padding = '='.repeat((4 - base64String.length % 4) % 4);
  var base64 = (base64String + padding)
    .replace(/\-/g, '+')
    .replace(/_/g, '/');

  var rawData = window.atob(base64);
  var outputArray = new Uint8Array(rawData.length);

  for (var i = 0; i < rawData.length; ++i) {
    outputArray[i] = rawData.charCodeAt(i);
  }
  return outputArray;
}

3
これにより、base64がUint8Arrayに変換されます。しかし、質問はUint8Arrayをbase64に変換する方法を尋ねます
Barry Michael Doyle

0

純粋なJS-文字列の中間ステップなし(btoaなし)

以下のソリューションでは、文字列への変換を省略しています。IDEAは次のとおりです。

  • 3バイト(3つの配列要素)を結合すると、24ビットが得られます
  • 24ビットを4つの6ビット数に分割します(0から63までの値を取ります)
  • その番号をbase64アルファベットのインデックスとして使用します
  • コーナーケース:入力バイト配列の長さが3で除算されていない場合、追加=または==結果

以下のソリューションは3バイトのチャンクで機能するため、大きな配列に適しています。base64をバイナリ配列(なしatob)に変換する同様のソリューションはこちらです


私はコンパクトさが好きですが、2進数を表す文字列に変換してから、元に戻すのは、受け入れられているソリューションよりもはるかに遅くなります。
ガーゴドフリー

0

以下を使用して、uint8配列をbase64でエンコードされた文字列に変換します

function arrayBufferToBase64(buffer) {
            var binary = '';
            var bytes = [].slice.call(new Uint8Array(buffer));
            bytes.forEach((b) => binary += String.fromCharCode(b));
            return window.btoa(binary);
        };


-3

必要なのがbase64エンコーダーのJS実装だけで、データを送り返すことができる場合は、このbtoa関数を試すことができます。

b64enc = btoa(uint);

btoaに関するいくつかの簡単なメモ-これは非標準であるため、ブラウザーはそれをサポートする必要はありません。ただし、ほとんどのブラウザはそうします。少なくとも大きなもの。atob逆の変換です。

別の実装が必要な場合、またはブラウザーが何について話しているのかわからないエッジケースを見つけた場合、JS用のbase64エンコーダーを検索するのはそれほど難しくありません。

どういうわけか、私の会社のウェブサイトに3つぶらぶらしていると思います...


おかげで、私は前にそれを試していませんでした。
カイオケト2012年

10
いくつかのメモ。btoaとatobは実際にはHTML5標準化プロセスの一部であり、ほとんどのブラウザーはすでにほとんど同じ方法でそれらをサポートしています。次に、btoaとatobは文字列でのみ機能します。Uint8Arrayでbtoaを実行すると、最初にtoString()を使用してバッファーが文字列に変換されます。これにより、文字列「[objectUint8Array]」が生成されます。それはおそらく意図されたものではありません。
kanaka 2012年

1
@CaioKeto選択した回答の変更を検討することをお勧めします。この答えは正しくありません。
カナカ

-4

npm install google-closure-library --save

require("google-closure-library");
goog.require('goog.crypt.base64');

var result =goog.crypt.base64.encodeByteArray(Uint8Array.of(1,83,27,99,102,66));
console.log(result);

$node index.jsAVMbY2Y =をコンソールに書き込みます。


1
-ve高い回答ではなく、投票された回答が受け入れられるのはおかしいです+ve
Vishnudev
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.