Ich habe eine webSocket-Kommunikation, ich erhalte eine Base64-codierte Zeichenfolge, konvertiere sie in uint8 und arbeite daran, aber jetzt muss ich zurücksenden, ich habe das uint8-Array und muss es in eine base64-Zeichenfolge konvertieren, damit ich es senden kann. Wie kann ich diese Konvertierung vornehmen?
javascript
base64
Caio Keto
quelle
quelle
Antworten:
Alle bereits vorgeschlagenen Lösungen weisen schwerwiegende Probleme auf. Einige Lösungen funktionieren nicht mit großen Arrays, andere liefern eine falsche Ausgabe, andere geben beim Btoa-Aufruf einen Fehler aus, wenn eine Zwischenzeichenfolge Multibyte-Zeichen enthält, andere verbrauchen mehr Speicher als benötigt.
Also habe ich eine direkte Konvertierungsfunktion implementiert, die unabhängig von der Eingabe funktioniert. Es konvertiert ungefähr 5 Millionen Bytes pro Sekunde auf meinem Computer.
https://gist.github.com/enepomnyaschih/72c423f727d395eeaa09697058238727
Code-Snippet anzeigen
/* 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)); }
quelle
"ABCDEFG..."
?Wenn Ihre Daten möglicherweise Mehrbyte -Sequenzen enthalten (keine einfache ASCII-Sequenz) und Ihr Browser über TextDecoder verfügt , sollten Sie diese zum Dekodieren Ihrer Daten verwenden (geben Sie die erforderliche Codierung für den TextDecoder an):
var u8 = new Uint8Array([65, 66, 67, 68]); var decoder = new TextDecoder('utf8'); var b64encoded = btoa(decoder.decode(u8));
Wenn Sie Browser unterstützen müssen, die keinen TextDecoder haben (derzeit nur IE und Edge), ist die beste Option die Verwendung einer TextDecoder-Polyfüllung .
Wenn Ihre Daten einfaches ASCII enthalten (kein Multibyte-Unicode / UTF-8), gibt es eine einfache Alternative
String.fromCharCode
, die allgemein unterstützt werden sollte:var ascii = new Uint8Array([65, 66, 67, 68]); var b64encoded = btoa(String.fromCharCode.apply(null, ascii));
Und um die base64-Zeichenfolge wieder in ein Uint8Array zu dekodieren:
var u8_2 = new Uint8Array(atob(b64encoded).split("").map(function(c) { return c.charCodeAt(0); }));
Wenn Sie über sehr große Array-Puffer verfügen, schlägt die Anwendung möglicherweise fehl und Sie müssen den Puffer möglicherweise aufteilen (basierend auf dem von @RohitSengar veröffentlichten Puffer). Beachten Sie erneut, dass dies nur korrekt ist, wenn Ihr Puffer nur Nicht-Multibyte-ASCII-Zeichen enthält:
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));
quelle
btoa(String.fromCharCode.apply(null, myArray))
Uint8Array
.TextDecoder
ist hier absolut falsch, denn wenn IhrUint8Array
Byte im Bereich 128..255 liegt, konvertiert der Textdecoder diese fälschlicherweise in Unicode-Zeichen, wodurch der base64-Konverter beschädigt wird.Sehr einfache Lösung und Test für 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));
quelle
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); }
Sie können diese Funktion verwenden, wenn Sie ein sehr großes Uint8Array haben. Dies ist für Javascript, kann im Fall von FileReader readAsArrayBuffer nützlich sein.
quelle
String.fromCharCode.apply()
Methoden können UTF-8 nicht reproduzieren: UTF-8-Zeichen können in der Länge von einem Byte bis zu vier Bytes variieren,String.fromCharCode.apply()
untersuchen jedoch ein UInt8Array in Segmenten von UInt8, sodass fälschlicherweise angenommen wird, dass jedes Zeichen genau ein Byte lang und unabhängig vom Nachbarn ist Einsen. Wenn sich die in der Eingabe UInt8Array codierten Zeichen alle zufällig im ASCII-Bereich (Einzelbyte) befinden, funktioniert dies zufällig, kann jedoch nicht die vollständige UTF-8 reproduzieren. Dazu benötigen Sie TextDecoder oder einen ähnlichen Algorithmus .Wenn Sie Node.js verwenden, können Sie diesen Code verwenden, um Uint8Array in base64 zu konvertieren
var b64 = Buffer.from(u8).toString('base64');
quelle
Hier ist eine JS-Funktion dazu:
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; }
quelle
Pure JS - kein String-Mittelschritt (kein Btoa)
In der folgenden Lösung lasse ich die Konvertierung in einen String aus. IDEE ist folgendes:
=
oder==
ergebenDie folgende Lösung funktioniert für 3-Byte-Blöcke, sodass sie für große Arrays geeignet ist. Eine ähnliche Lösung zum Konvertieren von base64 in ein binäres Array (ohne
atob
) finden Sie HIERCode-Snippet anzeigen
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));
quelle
Verwenden Sie Folgendes, um das uint8-Array in eine Base64-codierte Zeichenfolge zu konvertieren
function arrayBufferToBase64(buffer) { var binary = ''; var bytes = [].slice.call(new Uint8Array(buffer)); bytes.forEach((b) => binary += String.fromCharCode(b)); return window.btoa(binary); };
quelle
Siehe hier https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding#Appendix.3A_Decode_a_Base64_string_to_Uint8Array_or_ArrayBuffer
(Dekodieren Sie eine Base64-Zeichenfolge mit Unicode-Unterstützung in Uint8Array oder ArrayBuffer.)
quelle
Ein sehr guter Ansatz hierzu wird auf der Website des Mozilla Developer Network gezeigt :
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 "☸☹☺☻☼☾☿"
quelle
Wenn Sie nur eine JS-Implementierung eines Base64-Encoders benötigen, damit Sie Daten zurücksenden können, können Sie die
btoa
Funktion ausprobieren .Ein paar kurze Hinweise zu btoa - es ist nicht Standard, sodass Browser nicht gezwungen sind, es zu unterstützen. Die meisten Browser tun dies jedoch. Zumindest die Großen.
atob
ist die entgegengesetzte Umwandlung.Wenn Sie eine andere Implementierung benötigen oder einen Randfall finden, in dem der Browser keine Ahnung hat, wovon Sie sprechen, wäre die Suche nach einem Base64-Encoder für JS nicht allzu schwierig.
Ich denke, aus irgendeinem Grund hängen 3 davon auf der Website meines Unternehmens herum ...
quelle
npm installiere 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
würde AVMbY2Y = in die Konsole schreiben .quelle
-ve
abgestimmte Antwort eher akzeptiert wird als eine hohe+ve
.