webSocket通信を取得し、base64でエンコードされた文字列を受け取り、それをuint8に変換して処理しますが、今度は送信する必要があります。uint8配列を取得し、base64文字列に変換して送信できるようにします。どうすればこの変換を行うことができますか?
12 に答える
データにマルチバイトシーケンス(プレーン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));
Node.jsを使用している場合は、このコードを使用してUint8Arrayをbase64に変換できます
var u8 = new Uint8Array([65, 66, 67, 68]);
var b64 = Buffer.from(u8).toString('base64');
非常にシンプルなソリューションと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));
すでに提案されているすべてのソリューションには深刻な問題があります。大きな配列で機能しないソリューション、間違った出力を提供するソリューション、中間文字列にマルチバイト文字が含まれている場合にbtoa呼び出しでエラーをスローするソリューション、必要以上のメモリを消費するソリューションがあります。
そこで、入力に関係なく機能する直接変換関数を実装しました。私のマシンでは毎秒約500万バイトを変換します。
https://gist.github.com/enepomnyaschih/72c423f727d395eeaa09697058238727
/*
MIT License
Copyright (c) 2020 Egor Nepomnyaschih
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
// This constant can also be computed with the following algorithm:
const base64abc = [],
A = "A".charCodeAt(0),
a = "a".charCodeAt(0),
n = "0".charCodeAt(0);
for (let i = 0; i < 26; ++i) {
base64abc.push(String.fromCharCode(A + i));
}
for (let i = 0; i < 26; ++i) {
base64abc.push(String.fromCharCode(a + i));
}
for (let i = 0; i < 10; ++i) {
base64abc.push(String.fromCharCode(n + i));
}
base64abc.push("+");
base64abc.push("/");
*/
const base64abc = [
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "+", "/"
];
/*
// This constant can also be computed with the following algorithm:
const l = 256, base64codes = new Uint8Array(l);
for (let i = 0; i < l; ++i) {
base64codes[i] = 255; // invalid character
}
base64abc.forEach((char, index) => {
base64codes[char.charCodeAt(0)] = index;
});
base64codes["=".charCodeAt(0)] = 0; // ignored anyway, so we just need to prevent an error
*/
const base64codes = [
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 0, 255, 255,
255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
];
function getBase64Code(charCode) {
if (charCode >= base64codes.length) {
throw new Error("Unable to parse base64 string.");
}
const code = base64codes[charCode];
if (code === 255) {
throw new Error("Unable to parse base64 string.");
}
return code;
}
export function bytesToBase64(bytes) {
let result = '', i, l = bytes.length;
for (i = 2; i < l; i += 3) {
result += base64abc[bytes[i - 2] >> 2];
result += base64abc[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)];
result += base64abc[((bytes[i - 1] & 0x0F) << 2) | (bytes[i] >> 6)];
result += base64abc[bytes[i] & 0x3F];
}
if (i === l + 1) { // 1 octet yet to write
result += base64abc[bytes[i - 2] >> 2];
result += base64abc[(bytes[i - 2] & 0x03) << 4];
result += "==";
}
if (i === l) { // 2 octets yet to write
result += base64abc[bytes[i - 2] >> 2];
result += base64abc[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)];
result += base64abc[(bytes[i - 1] & 0x0F) << 2];
result += "=";
}
return result;
}
export function base64ToBytes(str) {
if (str.length % 4 !== 0) {
throw new Error("Unable to parse base64 string.");
}
const index = str.indexOf("=");
if (index !== -1 && index < str.length - 2) {
throw new Error("Unable to parse base64 string.");
}
let missingOctets = str.endsWith("==") ? 2 : str.endsWith("=") ? 1 : 0,
n = str.length,
result = new Uint8Array(3 * (n / 4)),
buffer;
for (let i = 0, j = 0; i < n; i += 4, j += 3) {
buffer =
getBase64Code(str.charCodeAt(i)) << 18 |
getBase64Code(str.charCodeAt(i + 1)) << 12 |
getBase64Code(str.charCodeAt(i + 2)) << 6 |
getBase64Code(str.charCodeAt(i + 3));
result[j] = buffer >> 16;
result[j + 1] = (buffer >> 8) & 0xFF;
result[j + 2] = buffer & 0xFF;
}
return result.subarray(0, result.length - missingOctets);
}
export function base64encode(str, encoder = new TextEncoder()) {
return bytesToBase64(encoder.encode(str));
}
export function base64decode(str, decoder = new TextDecoder()) {
return decoder.decode(base64ToBytes(str));
}
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の場合に役立ちます。
ネイティブブラウザソリューション(高速!)
Uint8Array
ネイティブブラウザ機能を使用して、任意のデータ(必ずしもUTF-8である必要はありません)でaをbase64エンコードするには:
const base64_arraybuffer = async (data) => {
// Use a FileReader to generate a base64 data URI
const base64url = await new Promise((r) => {
const reader = new FileReader()
reader.onload = () => r(reader.result)
reader.readAsDataURL(new Blob([data]))
})
/*
The result looks like
"data:application/octet-stream;base64,<your base64 data>",
so we split off the beginning:
*/
return base64url.split(",", 2)[1]
}
// example use:
await base64_arraybuffer(new Uint8Array([1,2,3,100,200]))
これはネイティブブラウザ機能を使用しているため、パフォーマンスは最適です。私のコンピューターでは毎秒250MBを変換でき(ベンチマークスクリプト)、受け入れられた回答よりも約50倍高速になります。
純粋なJS-文字列の中間ステップなし(btoaなし)
以下のソリューションでは、文字列への変換を省略しています。IDEAは次のとおりです。
- 3バイト(3つの配列要素)を結合すると、24ビットが得られます
- 24ビットを4つの6ビット数に分割します(0から63までの値を取ります)
- その番号をbase64アルファベットのインデックスとして使用します
- コーナーケース:入力バイト配列が3で除算されていない場合、追加
=
または==
結果
以下のソリューションは3バイトのチャンクで機能するため、大きな配列に適しています。base64をバイナリ配列(なしatob
)に変換する同様のソリューションはここにあります
function bytesArrToBase64(arr) {
const abc = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; // base64 alphabet
const bin = n => n.toString(2).padStart(8,0); // convert num to 8-bit binary string
const l = arr.length
let result = '';
for(let i=0; i<=(l-1)/3; i++) {
let c1 = i*3+1>=l; // case when "=" is on end
let c2 = i*3+2>=l; // case when "=" is on end
let chunk = bin(arr[3*i]) + bin(c1? 0:arr[3*i+1]) + bin(c2? 0:arr[3*i+2]);
let r = chunk.match(/.{1,6}/g).map((x,j)=> j==3&&c2 ? '=' :(j==2&&c1 ? '=':abc[+('0b'+x)]));
result += r.join('');
}
return result;
}
// ----------
// TEST
// ----------
let test = "Alice's Adventure in Wondeland.";
let testBytes = [...test].map(c=> c.charCodeAt(0) );
console.log('test string:', test);
console.log('bytes:', JSON.stringify(testBytes));
console.log('btoa ', btoa(test));
console.log('bytesArrToBase64', bytesArrToBase64(testBytes));
以下を使用して、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);
};
これに対する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;
}
(Base64文字列をUnicodeをサポートするUint8ArrayまたはArrayBufferにデコードします)
必要なのがbase64エンコーダーのJS実装だけで、データを送り返すことができる場合は、このbtoa
関数を試すことができます。
b64enc = btoa(uint);
btoaに関するいくつかの簡単なメモ-これは非標準であるため、ブラウザーはbtoaをサポートする必要はありません。ただし、ほとんどのブラウザはそうします。少なくとも大きなもの。atob
反対の変換です。
別の実装が必要な場合、またはブラウザーが何について話しているのかわからないエッジケースを見つけた場合、JS用のbase64エンコーダーを検索するのはそれほど難しくありません。
どういうわけか、私の会社のウェブサイトには3つあると思います...
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.js
AVMbY2Y=をコンソールに書き込みます。