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?
javascript
arrays
base64
Caio Keto
źródło
źródło
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
Pokaż fragment kodu
/* 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)); }
źródło
"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));
źródło
btoa(String.fromCharCode.apply(null, myArray))
Uint8Array
.TextDecoder
jest tutaj absolutnie niewłaściwą rzeczą, ponieważ jeśli maszUint8Array
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));
źródło
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.
źródło
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, aleString.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');
źródło
Oto funkcja JS do tego:
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; }
źródło
Czysty JS - bez łańcucha po środku (bez btoa)
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 TUTAJPokaż fragment kodu
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));
źródło
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); };
źródło
Zobacz tutaj https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding#Appendix.3A_Decode_a_Base64_string_to_Uint8Array_or_ArrayBuffer
(Dekoduj ciąg Base64 do Uint8Array lub ArrayBuffer z obsługą Unicode)
źródło
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 "☸☹☺☻☼☾☿"
źródło
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ę.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 ...
źródło
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.źródło
-ve
głosowana odpowiedź jest akceptowana, a nie wysoce+ve
.