Mam komunikację webSocket, otrzymuję ciąg zakodowany w base64, konwertuję go na uint8 i pracuję nad nim, ale teraz muszę odesłać, mam tablicę uint8 i muszę przekonwertować ją na ciąg base64, więc mogę to wysłać. Jak mogę dokonać tej konwersji?
Mam komunikację webSocket, otrzymuję ciąg zakodowany w base64, konwertuję go na uint8 i pracuję nad nim, ale teraz muszę odesłać, mam tablicę uint8 i muszę przekonwertować ją na ciąg base64, więc mogę to wysłać. Jak mogę dokonać tej konwersji?
Odpowiedzi:
Wszystkie proponowane rozwiązania mają poważne problemy. Niektóre rozwiązania nie działają na dużych tablicach, niektóre zapewniają błędne dane wyjściowe, inne generują błąd w wywołaniu btoa, jeśli łańcuch pośredni zawiera znaki wielobajtowe, a niektóre zajmują więcej pamięci niż potrzeba.
Zaimplementowałem więc funkcję konwersji bezpośredniej, która działa niezależnie od danych wejściowych. Konwertuje około 5 milionów bajtów na sekundę na moim komputerze.
https://gist.github.com/enepomnyaschih/72c423f727d395eeaa09697058238727
"ABCDEFG..."
?
Jeśli Twoje dane mogą zawierać sekwencje wielobajtowe (a nie zwykłą sekwencję ASCII), a Twoja przeglądarka ma TextDecoder , powinieneś użyć tego do dekodowania danych (określ wymagane kodowanie dla TextDecoder):
var u8 = new Uint8Array([65, 66, 67, 68]);
var decoder = new TextDecoder('utf8');
var b64encoded = btoa(decoder.decode(u8));
Jeśli potrzebujesz obsługiwać przeglądarki, które nie mają TextDecodera (obecnie tylko IE i Edge), najlepszą opcją jest użycie wypełnienia TextDecoder .
Jeśli twoje dane zawierają zwykły ASCII (nie wielobajtowy Unicode / UTF-8), istnieje prosta alternatywa, String.fromCharCode
która powinna być powszechnie obsługiwana:
var ascii = new Uint8Array([65, 66, 67, 68]);
var b64encoded = btoa(String.fromCharCode.apply(null, ascii));
Aby zdekodować ciąg base64 z powrotem do Uint8Array:
var u8_2 = new Uint8Array(atob(b64encoded).split("").map(function(c) {
return c.charCodeAt(0); }));
Jeśli masz bardzo duże bufory tablicowe, zastosowanie może się nie powieść i może być konieczne podzielenie buforu (na podstawie tego opublikowanego przez @RohitSengar). Zwróć uwagę, że jest to poprawne tylko wtedy, gdy twój bufor zawiera tylko inne niż wielobajtowe znaki 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));
btoa(String.fromCharCode.apply(null, myArray))
Uint8Array
. TextDecoder
jest tutaj absolutnie niewłaściwą rzeczą, ponieważ jeśli masz Uint8Array
bajty w zakresie 128..255, dekoder tekstu błędnie przekonwertuje je na znaki Unicode, co zepsuje konwerter base64.
Bardzo proste rozwiązanie i test na 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));
RangeError: Maximum call stack size exceeded
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);
}
Możesz użyć tej funkcji, jeśli masz bardzo duży Uint8Array. To jest dla Javascript, może być przydatne w przypadku FileReader readAsArrayBuffer.
String.fromCharCode.apply()
Metody @Jens nie mogą odtworzyć UTF-8: znaki UTF-8 mogą mieć różną długość od jednego bajtu do czterech bajtów, ale String.fromCharCode.apply()
analizuje UInt8Array w segmentach UInt8, więc błędnie zakłada, że każdy znak ma dokładnie jeden bajt długości i jest niezależny od sąsiedniego jedynki. Jeśli wszystkie znaki zakodowane w wejściowym UInt8Array znajdują się w zakresie ASCII (jednobajtowym), zadziała to przez przypadek, ale nie może odtworzyć pełnego UTF-8. Do tego potrzebny jest TextDecoder lub podobny algorytm .
Jeśli używasz Node.js, możesz użyć tego kodu, aby przekonwertować Uint8Array na base64
var b64 = Buffer.from(u8).toString('base64');
Oto funkcja JS do tego:
Ta funkcja jest potrzebna, ponieważ Chrome nie akceptuje ciągu znaków zakodowanych w base64 jako wartości dla applicationServerKey w pushManager.subscribe 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;
}
W poniższym rozwiązaniu pomijam konwersję na string. IDEA jest następująca:
=
lub ==
do wynikuPoniższe rozwiązanie działa na fragmentach 3-bajtowych, więc jest dobre dla dużych tablic. Podobne rozwiązanie do konwersji base64 na tablicę binarną (bez atob
) jest TUTAJ
Użyj poniższego, aby przekonwertować tablicę uint8 na ciąg zakodowany algorytmem Base64
function arrayBufferToBase64(buffer) {
var binary = '';
var bytes = [].slice.call(new Uint8Array(buffer));
bytes.forEach((b) => binary += String.fromCharCode(b));
return window.btoa(binary);
};
(Dekoduj ciąg Base64 do Uint8Array lub ArrayBuffer z obsługą Unicode)
Bardzo dobre podejście do tego jest pokazane na stronie Mozilla Developer Network :
function btoaUTF16 (sString) {
var aUTF16CodeUnits = new Uint16Array(sString.length);
Array.prototype.forEach.call(aUTF16CodeUnits, function (el, idx, arr) { arr[idx] = sString.charCodeAt(idx); });
return btoa(String.fromCharCode.apply(null, new Uint8Array(aUTF16CodeUnits.buffer)));
}
function atobUTF16 (sBase64) {
var sBinaryString = atob(sBase64), aBinaryView = new Uint8Array(sBinaryString.length);
Array.prototype.forEach.call(aBinaryView, function (el, idx, arr) { arr[idx] = sBinaryString.charCodeAt(idx); });
return String.fromCharCode.apply(null, new Uint16Array(aBinaryView.buffer));
}
var myString = "☸☹☺☻☼☾☿";
var sUTF16Base64 = btoaUTF16(myString);
console.log(sUTF16Base64); // Shows "OCY5JjomOyY8Jj4mPyY="
var sDecodedString = atobUTF16(sUTF16Base64);
console.log(sDecodedString); // Shows "☸☹☺☻☼☾☿"
Jeśli wszystko, czego chcesz, to implementacja kodera base64 w JS, abyś mógł przesłać dane z powrotem, możesz wypróbować tę btoa
funkcję.
b64enc = btoa(uint);
Kilka krótkich uwag na temat btoa - jest niestandardowy, więc przeglądarki nie są zmuszane do jego obsługi. Jednak większość przeglądarek to robi. Przynajmniej te duże. atob
jest odwrotną konwersją.
Jeśli potrzebujesz innej implementacji lub znajdziesz przypadek skrajny, w którym przeglądarka nie ma pojęcia, o czym mówisz, wyszukiwanie kodera base64 dla JS nie byłoby zbyt trudne.
Myślę, że z jakiegoś powodu na stronie mojej firmy kręcą się 3 z nich ...
npm zainstaluj 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
zapisze AVMbY2Y = na konsoli.
-ve
głosowana odpowiedź jest akceptowana, a nie wysoce +ve
.