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.fromCharCodektó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. TextDecoderjest tutaj absolutnie niewłaściwą rzeczą, ponieważ jeśli masz Uint8Arraybajty 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ę btoafunkcję.
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. atobjest 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.jszapisze AVMbY2Y = na konsoli.
-vegłosowana odpowiedź jest akceptowana, a nie wysoce +ve.