J'ai une communication webSocket, je reçois une chaîne encodée en base64, je la convertis en uint8 et je travaille dessus, mais maintenant je dois renvoyer, j'ai le tableau uint8 et je dois le convertir en chaîne base64, afin que je puisse l'envoyer. Comment puis-je effectuer cette conversion?
javascript
arrays
base64
Caio Keto
la source
la source
Réponses:
Toutes les solutions déjà proposées présentent de graves problèmes. Certaines solutions ne fonctionnent pas sur de grands tableaux, certaines fournissent une sortie erronée, certaines lancent une erreur sur un appel btoa si une chaîne intermédiaire contient des caractères multi-octets, certaines consomment plus de mémoire que nécessaire.
J'ai donc implémenté une fonction de conversion directe qui fonctionne juste indépendamment de l'entrée. Il convertit environ 5 millions d'octets par seconde sur ma machine.
https://gist.github.com/enepomnyaschih/72c423f727d395eeaa09697058238727
Afficher l'extrait de code
/* 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)); }
la source
"ABCDEFG..."
?Si vos données peuvent contenir des séquences multi-octets (pas une séquence ASCII simple) et que votre navigateur a TextDecoder , vous devez l'utiliser pour décoder vos données (spécifiez l'encodage requis pour TextDecoder):
var u8 = new Uint8Array([65, 66, 67, 68]); var decoder = new TextDecoder('utf8'); var b64encoded = btoa(decoder.decode(u8));
Si vous devez prendre en charge les navigateurs qui n'ont pas TextDecoder (actuellement juste IE et Edge), la meilleure option est d'utiliser un polyfill TextDecoder .
Si vos données contiennent de l'ASCII brut (pas Unicode / UTF-8 multi-octets), il existe une alternative simple d'utilisation
String.fromCharCode
qui devrait être prise en charge de manière assez universelle:var ascii = new Uint8Array([65, 66, 67, 68]); var b64encoded = btoa(String.fromCharCode.apply(null, ascii));
Et pour décoder la chaîne base64 en un Uint8Array:
var u8_2 = new Uint8Array(atob(b64encoded).split("").map(function(c) { return c.charCodeAt(0); }));
Si vous avez des tampons de tableau très volumineux, l'application peut échouer et vous devrez peut-être segmenter le tampon (en fonction de celui publié par @RohitSengar). Encore une fois, notez que cela n'est correct que si votre tampon contient uniquement des caractères ASCII non multi-octets:
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));
la source
btoa(String.fromCharCode.apply(null, myArray))
Uint8Array
.TextDecoder
est absolument la mauvaise chose à utiliser ici, car si votreUint8Array
a octets dans la plage 128..255, le décodeur de texte les convertira par erreur en caractères unicode, ce qui cassera le convertisseur base64.Solution très simple et test pour 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));
la source
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); }
Vous pouvez utiliser cette fonction si vous avez un très grand Uint8Array. Ceci est pour Javascript, peut être utile dans le cas de FileReader readAsArrayBuffer.
la source
String.fromCharCode.apply()
méthodes @Jens ne peuvent pas reproduire UTF-8: les caractères UTF-8 peuvent varier en longueur d'un octet à quatre octets, maisString.fromCharCode.apply()
examine un UInt8Array dans des segments de UInt8, donc il suppose à tort que chaque caractère est exactement un octet de long et indépendant du voisin ceux. Si les caractères encodés dans l'entrée UInt8Array se trouvent tous dans la plage ASCII (à un octet), cela fonctionnera par hasard, mais il ne pourra pas reproduire l'UTF-8 complet. Vous avez besoin de TextDecoder ou d'un algorithme similaire pour cela.Si vous utilisez Node.js, vous pouvez utiliser ce code pour convertir Uint8Array en base64
var b64 = Buffer.from(u8).toString('base64');
la source
Voici une fonction JS à ceci:
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; }
la source
Pure JS - pas de chaîne intermédiaire (pas de btoa)
Dans la solution ci-dessous, j'omets la conversion en chaîne. IDEA suit:
=
ou==
au résultatLa solution ci-dessous fonctionne sur des blocs de 3 octets, elle convient donc aux grands tableaux. Une solution similaire pour convertir base64 en tableau binaire (sans
atob
) est ICIAfficher l'extrait de code
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));
la source
Utilisez ce qui suit pour convertir le tableau uint8 en chaîne encodée en base64
function arrayBufferToBase64(buffer) { var binary = ''; var bytes = [].slice.call(new Uint8Array(buffer)); bytes.forEach((b) => binary += String.fromCharCode(b)); return window.btoa(binary); };
la source
Voir ici https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding#Appendix.3A_Decode_a_Base64_string_to_Uint8Array_or_ArrayBuffer
(Décodez une chaîne Base64 en Uint8Array ou ArrayBuffer avec prise en charge Unicode)
la source
Une très bonne approche à ce sujet est présentée sur le site Web de 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 "☸☹☺☻☼☾☿"
la source
Si tout ce que vous voulez est une implémentation JS d'un encodeur base64, afin que vous puissiez renvoyer des données, vous pouvez essayer la
btoa
fonction.Quelques notes rapides sur le btoa - ce n'est pas standard, donc les navigateurs ne sont pas obligés de le prendre en charge. Cependant, la plupart des navigateurs le font. Les gros, au moins.
atob
est la conversion opposée.Si vous avez besoin d'une implémentation différente, ou si vous trouvez un cas de pointe où le navigateur n'a aucune idée de ce dont vous parlez, la recherche d'un encodeur base64 pour JS ne serait pas trop difficile.
Je pense qu'il y en a 3 qui traînent sur le site Web de mon entreprise, pour une raison quelconque ...
la source
npm installer google-closing-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
écrirait AVMbY2Y = sur la console.la source
-ve
réponse votée soit acceptée plutôt qu'une réponse hautement+ve
.