ฉันได้รับการสื่อสารแบบ webSocket ฉันได้รับสตริงที่เข้ารหัส base64 แปลงเป็น uint8 และทำงานกับมัน แต่ตอนนี้ฉันต้องส่งกลับฉันได้รับอาร์เรย์ uint8 และต้องแปลงเป็นสตริง base64 ฉันจึงจะสามารถส่งได้ ฉันจะทำการเปลี่ยนแปลงนี้ได้อย่างไร?
ฉันได้รับการสื่อสารแบบ webSocket ฉันได้รับสตริงที่เข้ารหัส base64 แปลงเป็น uint8 และทำงานกับมัน แต่ตอนนี้ฉันต้องส่งกลับฉันได้รับอาร์เรย์ uint8 และต้องแปลงเป็นสตริง base64 ฉันจึงจะสามารถส่งได้ ฉันจะทำการเปลี่ยนแปลงนี้ได้อย่างไร?
คำตอบ:
แนวทางแก้ไขทั้งหมดที่เสนอไปแล้วมีปัญหารุนแรง โซลูชันบางอย่างไม่สามารถทำงานกับอาร์เรย์ขนาดใหญ่บางตัวให้เอาต์พุตผิดพลาดบางตัวเกิดข้อผิดพลาดในการเรียก btoa หากสตริงกลางมีอักขระหลายไบต์บางตัวใช้หน่วยความจำมากกว่าที่จำเป็น
ดังนั้นฉันจึงใช้ฟังก์ชันการแปลงโดยตรงซึ่งใช้งานได้โดยไม่คำนึงถึงอินพุต มันแปลงประมาณ 5 ล้านไบต์ต่อวินาทีบนเครื่องของฉัน
https://gist.github.com/enepomnyaschih/72c423f727d395eeaa09697058238727
"ABCDEFG..."
เหรอ?
หากข้อมูลของคุณอาจมีลำดับแบบหลายไบต์ (ไม่ใช่ลำดับ ASCII ธรรมดา) และเบราว์เซอร์ของคุณมีTextDecoderคุณควรใช้ข้อมูลนั้นเพื่อถอดรหัสข้อมูลของคุณ (ระบุการเข้ารหัสที่จำเป็นสำหรับ TextDecoder):
var u8 = new Uint8Array([65, 66, 67, 68]);
var decoder = new TextDecoder('utf8');
var b64encoded = btoa(decoder.decode(u8));
หากคุณต้องการการสนับสนุนเบราว์เซอร์ที่ไม่ได้มี TextDecoder (ปัจจุบันเพียง IE และขอบ) จากนั้นเลือกที่ดีที่สุดคือการใช้polyfill TextDecoder
หากข้อมูลของคุณมี ASCII ธรรมดา (ไม่ใช่หลายไบต์ Unicode / UTF-8) แสดงว่ามีทางเลือกง่ายๆในการใช้String.fromCharCode
ที่ควรได้รับการสนับสนุนอย่างเป็นธรรม:
var ascii = new Uint8Array([65, 66, 67, 68]);
var b64encoded = btoa(String.fromCharCode.apply(null, ascii));
และในการถอดรหัสสตริง base64 กลับไปเป็น Uint8Array:
var u8_2 = new Uint8Array(atob(b64encoded).split("").map(function(c) {
return c.charCodeAt(0); }));
หากคุณมีบัฟเฟอร์อาร์เรย์ขนาดใหญ่มากการใช้งานอาจล้มเหลวและคุณอาจต้องแบ่งบัฟเฟอร์ (ตามที่โพสต์โดย @RohitSengar) โปรดทราบอีกครั้งว่านี่จะถูกต้องก็ต่อเมื่อบัฟเฟอร์ของคุณมีเฉพาะอักขระ 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
หรือในที่มี TextDecoder
เป็นสิ่งที่ผิดอย่างยิ่งที่จะใช้ที่นี่เพราะถ้าคุณUint8Array
มีไบต์ในช่วง 128..255 ตัวถอดรหัสข้อความจะแปลงเป็นอักขระ Unicode อย่างผิดพลาดซึ่งจะทำลายตัวแปลง base64
วิธีแก้ปัญหาและทดสอบ 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
หากคุณใช้ Node.js คุณสามารถใช้รหัสนี้เพื่อแปลง Uint8Array เป็น base64
var b64 = Buffer.from(u8).toString('base64');
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);
}
คุณสามารถใช้ฟังก์ชันนี้ได้หากคุณมี Uint8Array ขนาดใหญ่มาก สิ่งนี้มีไว้สำหรับ Javascript ซึ่งมีประโยชน์ในกรณีของ FileReader readAsArrayBuffer
String.fromCharCode.apply()
วิธีการของ@Jens ไม่สามารถสร้าง UTF-8 ได้: อักขระ UTF-8 อาจมีความยาวแตกต่างกันไปตั้งแต่หนึ่งไบต์ถึงสี่ไบต์ แต่String.fromCharCode.apply()
ตรวจสอบ UInt8Array ในส่วนของ UInt8 ดังนั้นจึงถือว่าอักขระแต่ละตัวมีความยาวหนึ่งไบต์โดยไม่ขึ้นกับเพื่อนบ้าน คน หากอักขระที่เข้ารหัสใน UInt8Array อินพุตทั้งหมดอยู่ในช่วง ASCII (ไบต์เดียว) จะทำงานโดยบังเอิญ แต่ไม่สามารถสร้าง UTF-8 แบบเต็มได้ คุณต้องมี TextDecoder หรืออัลกอริทึมที่คล้ายกันสำหรับสิ่งนั้น
นี่คือฟังก์ชัน JS สำหรับสิ่งนี้:
จำเป็นต้องใช้ฟังก์ชันนี้เนื่องจาก Chrome ไม่ยอมรับสตริงที่เข้ารหัส base64 เป็นค่าสำหรับ applicationServerKey ใน 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;
}
ในโซลูชันด้านล่างฉันละเว้นการแปลงเป็นสตริง IDEA กำลังติดตาม:
=
หรือ==
เพื่อผลลัพธ์โซลูชันด้านล่างใช้งานได้กับชิ้นขนาด 3 ไบต์จึงเหมาะสำหรับอาร์เรย์ขนาดใหญ่ โซลูชันที่คล้ายกันในการแปลง base64 เป็นอาร์เรย์ไบนารี (ไม่มีatob
) อยู่ที่นี่
ใช้สิ่งต่อไปนี้เพื่อแปลงอาร์เรย์ uint8 เป็นสตริงที่เข้ารหัส base64
function arrayBufferToBase64(buffer) {
var binary = '';
var bytes = [].slice.call(new Uint8Array(buffer));
bytes.forEach((b) => binary += String.fromCharCode(b));
return window.btoa(binary);
};
(ถอดรหัสสตริง Base64 เป็น Uint8Array หรือ ArrayBuffer พร้อมรองรับ Unicode)
หากคุณต้องการเพียงแค่ใช้ JS ของตัวเข้ารหัส base64 เพื่อให้คุณสามารถส่งข้อมูลกลับได้คุณสามารถลองใช้btoa
ฟังก์ชันนี้ได้
b64enc = btoa(uint);
บันทึกย่อสองสามข้อเกี่ยวกับ btoa - มันไม่ได้มาตรฐานดังนั้นเบราว์เซอร์จึงไม่ได้บังคับให้รองรับ อย่างไรก็ตามเบราว์เซอร์ส่วนใหญ่ทำ คนตัวใหญ่อย่างน้อย atob
คือการแปลงตรงกันข้าม
หากคุณต้องการการใช้งานแบบอื่นหรือคุณพบกรณีขอบที่เบราว์เซอร์ไม่รู้ว่าคุณกำลังพูดถึงอะไรการค้นหาตัวเข้ารหัส base64 สำหรับ JS จะไม่ยากเกินไป
ฉันคิดว่ามี 3 คนที่แขวนอยู่บนเว็บไซต์ของ บริษัท ด้วยเหตุผลบางอย่าง
npm ติดตั้ง google-closed-library - บันทึก
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
จะเขียนAVMbY2Y =ลงในคอนโซล
-ve
คำตอบที่ได้รับการโหวตเป็นที่ยอมรับมากกว่าสูง+ve
หนึ่ง