Saya mendapat komunikasi webSocket, saya menerima string yang dikodekan base64, mengubahnya menjadi uint8 dan mengerjakannya, tetapi sekarang saya perlu mengirim kembali, saya mendapatkan array uint8, dan perlu mengubahnya menjadi string base64, jadi saya dapat mengirimnya. Bagaimana saya dapat melakukan konversi ini?
javascript
arrays
base64
Caio Keto
sumber
sumber
Jawaban:
Semua solusi yang sudah diusulkan memiliki masalah yang parah. Beberapa solusi gagal bekerja pada array besar, beberapa memberikan output yang salah, beberapa memberikan kesalahan pada panggilan btoa jika string perantara berisi karakter multibyte, beberapa menggunakan lebih banyak memori daripada yang dibutuhkan.
Jadi saya menerapkan fungsi konversi langsung yang hanya berfungsi terlepas dari inputnya. Ini mengubah sekitar 5 juta byte per detik di mesin saya.
https://gist.github.com/enepomnyaschih/72c423f727d395eeaa09697058238727
Tampilkan cuplikan kode
/* 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)); }
sumber
"ABCDEFG..."
?Jika data Anda mungkin berisi urutan multi-byte (bukan urutan ASCII biasa) dan browser Anda memiliki TextDecoder , Anda harus menggunakannya untuk mendekode data Anda (tentukan encoding yang diperlukan untuk TextDecoder):
var u8 = new Uint8Array([65, 66, 67, 68]); var decoder = new TextDecoder('utf8'); var b64encoded = btoa(decoder.decode(u8));
Jika Anda perlu mendukung browser yang tidak memiliki TextDecoder (saat ini hanya IE dan Edge), maka opsi terbaik adalah menggunakan polyfill TextDecoder .
Jika data Anda berisi ASCII biasa (bukan multibyte Unicode / UTF-8) maka ada alternatif sederhana yang menggunakan
String.fromCharCode
yang seharusnya cukup didukung secara universal:var ascii = new Uint8Array([65, 66, 67, 68]); var b64encoded = btoa(String.fromCharCode.apply(null, ascii));
Dan untuk mendekode string base64 kembali ke Uint8Array:
var u8_2 = new Uint8Array(atob(b64encoded).split("").map(function(c) { return c.charCodeAt(0); }));
Jika Anda memiliki buffer array yang sangat besar maka penerapan mungkin gagal dan Anda mungkin perlu memotong buffer (berdasarkan yang diposting oleh @RohitSengar). Sekali lagi, perhatikan bahwa ini hanya benar jika buffer Anda hanya berisi karakter ASCII non-multibyte:
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));
sumber
btoa(String.fromCharCode.apply(null, myArray))
Uint8Array
.TextDecoder
benar-benar hal yang salah untuk digunakan di sini, karena jika AndaUint8Array
memiliki byte dalam kisaran 128..255, dekoder teks akan secara keliru mengubahnya menjadi karakter unicode, yang akan merusak konverter base64.Solusi dan uji yang sangat sederhana untuk 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));
sumber
RangeError: Maximum call stack size exceeded
Jika Anda menggunakan Node.js maka Anda dapat menggunakan kode ini untuk mengubah Uint8Array menjadi base64
var b64 = Buffer.from(u8).toString('base64');
sumber
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); }
Anda dapat menggunakan fungsi ini jika Anda memiliki Uint8Array yang sangat besar. Ini untuk Javascript, dapat berguna dalam kasus FileReader readAsArrayBuffer.
sumber
String.fromCharCode.apply()
Metode @Jens tidak dapat mereproduksi UTF-8: Karakter UTF-8 dapat bervariasi panjangnya dari satu byte hingga empat byte, namunString.fromCharCode.apply()
memeriksa UInt8Array dalam segmen UInt8, sehingga secara keliru mengasumsikan setiap karakter menjadi tepat satu byte dan independen dari tetangganya satu. Jika karakter yang dikodekan dalam input UInt8Array semuanya berada dalam rentang ASCII (single-byte), ini akan bekerja secara kebetulan, tetapi tidak dapat mereproduksi UTF-8 penuh. Anda membutuhkan TextDecoder atau algoritme serupa untuk itu.Berikut adalah Fungsi JS untuk ini:
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; }
sumber
JS murni - tanpa string middlestep (tanpa btoa)
Dalam solusi di bawah ini saya menghilangkan konversi ke string. IDEA adalah sebagai berikut:
=
atau==
menghasilkanSolusi di bawah ini berfungsi pada potongan 3-byte sehingga bagus untuk array besar. Solusi serupa untuk mengonversi base64 ke array biner (tanpa
atob
) ada DI SINITampilkan cuplikan kode
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));
sumber
Gunakan perintah berikut untuk mengonversi larik uint8 menjadi string berenkode base64
function arrayBufferToBase64(buffer) { var binary = ''; var bytes = [].slice.call(new Uint8Array(buffer)); bytes.forEach((b) => binary += String.fromCharCode(b)); return window.btoa(binary); };
sumber
Lihat di sini https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding#Appendix.3A_Decode_a_Base64_string_to_Uint8Array_or_ArrayBuffer
(Dekode string Base64 ke Uint8Array atau ArrayBuffer dengan dukungan Unicode)
sumber
Jika yang Anda inginkan hanyalah implementasi JS dari encoder base64, sehingga Anda dapat mengirim data kembali, Anda dapat mencoba
btoa
fungsinya.Beberapa catatan singkat tentang btoa - ini tidak standar, jadi browser tidak dipaksa untuk mendukungnya. Namun, kebanyakan browser melakukannya. Setidaknya yang besar.
atob
adalah kebalikan dari pertobatan.Jika Anda memerlukan implementasi yang berbeda, atau Anda menemukan kasus tepi di mana browser tidak tahu apa yang Anda bicarakan, mencari encoder base64 untuk JS tidak akan terlalu sulit.
Saya pikir ada 3 dari mereka yang berkeliaran di situs web perusahaan saya, untuk beberapa alasan ...
sumber
npm instal 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
akan menulis AVMbY2Y = ke konsol.sumber
-ve
jawaban yang dipilih diterima daripada yang sangat tinggi+ve
.