Uint8 Dizisini base64 Kodlanmış Dizeye nasıl dönüştürebilirim?


90

Bir webSocket iletişimim var, base64 kodlu dizeyi alıyorum, onu uint8'e dönüştürüyorum ve üzerinde çalışıyorum, ancak şimdi geri göndermem gerekiyor, uint8 dizisini aldım ve onu gönderebilmem için base64 dizesine dönüştürmem gerekiyor. Bu dönüşümü nasıl yapabilirim?



"ArrayBuffer'dan base64 kodlanmış dizeye" sorusu, tüm karakterleri işleyen daha iyi bir çözüm içerir. stackoverflow.com/questions/9267899/…
Steve Hanov

Yanıtlar:


16

Önceden önerilen tüm çözümlerin ciddi sorunları vardır. Bazı çözümler büyük dizilerde çalışamaz, bazıları yanlış çıktı sağlar, bazıları bir ara dizge çok baytlı karakterler içeriyorsa, bazıları btoa çağrısında hata verir, bazıları gerekenden daha fazla bellek tüketir.

Bu yüzden girdiden bağımsız olarak çalışan bir doğrudan dönüştürme işlevi uyguladım. Makinemde saniyede yaklaşık 5 milyon baytı dönüştürüyor.

https://gist.github.com/enepomnyaschih/72c423f727d395eeaa09697058238727


Bir dizi dizisi olarak base64abc'ye sahip olmak, onu bir dizge yapmaktan daha mı hızlıdır? "ABCDEFG..."?
Garr Godfrey

163

Verileriniz çok baytlı diziler içeriyorsa (düz bir ASCII dizisi değil) ve tarayıcınızda TextDecoder varsa, verilerinizin kodunu çözmek için bunu kullanmalısınız (TextDecoder için gerekli kodlamayı belirtin):

var u8 = new Uint8Array([65, 66, 67, 68]);
var decoder = new TextDecoder('utf8');
var b64encoded = btoa(decoder.decode(u8));

TextDecoder'a (şu anda yalnızca IE ve Edge) sahip olmayan tarayıcıları desteklemeniz gerekiyorsa , en iyi seçenek bir TextDecoder çoklu dolgusu kullanmaktır .

Verileriniz düz ASCII (çok baytlı Unicode / UTF-8 değil) String.fromCharCodeiçeriyorsa, oldukça evrensel olarak desteklenmesi gereken basit bir alternatif vardır :

var ascii = new Uint8Array([65, 66, 67, 68]);
var b64encoded = btoa(String.fromCharCode.apply(null, ascii));

Ve base64 dizesinin kodunu bir Uint8Array'e geri çözmek için:

var u8_2 = new Uint8Array(atob(b64encoded).split("").map(function(c) {
    return c.charCodeAt(0); }));

Çok büyük dizi arabellekleriniz varsa, uygulama başarısız olabilir ve arabelleği parçalamanız gerekebilir (@RohitSengar tarafından gönderilene göre). Yine, bunun yalnızca arabelleğiniz yalnızca çok baytlı olmayan ASCII karakterler içeriyorsa doğru olduğunu unutmayın:

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));

4
Bu benim için Firefox'ta çalışıyor, ancak Chrome "Yakalanmamış Aralık Hatası: Maksimum çağrı yığını boyutu aşıldı" ile boğuluyor (btoa yapıyor).
Michael Paulukonis

3
@MichaelPaulukonis benim tahminime göre yığın boyutunun aşılmasına neden olan aslında String.fromCharCode.apply. Çok büyük bir Uint8Array'ınız varsa, bunu yapmak için apply kullanmak yerine dizeyi yinelemeli olarak oluşturmanız gerekecektir. Apply () çağrısı dizinizin her öğesini bir parametre olarak fromCharCode'a iletir, bu nedenle dizi 128000 bayt uzunluğundaysa, 128000 parametreli bir işlev çağrısı yapmaya çalışırsınız ve bu da muhtemelen yığını patlatır.
kanaka

4
Teşekkürler. Tek ihtiyacım olanbtoa(String.fromCharCode.apply(null, myArray))
Glen Little

29
Bayt dizisi geçerli Unicode değilse bu çalışmaz.
Melab

11
Base64 dizesinde veya içinde çok baytlı karakter yoktur Uint8Array. TextDecoderburada kullanmak kesinlikle yanlış bir şeydir, çünkü Uint8Array128..255 aralığında baytlarınız varsa , metin kod çözücü bunları yanlışlıkla unicode karakterlere dönüştürür ve bu da base64 dönüştürücüyü bozar.
riv

26

JavaScript için çok basit bir çözüm ve test!

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));

4
En temiz çözüm!
realappie

Mükemmel çözüm
Haris ur Rehman

2
büyük verilerde (resimler gibi) başarısız oluyorRangeError: Maximum call stack size exceeded
Maxim Khokhryakov

21

Node.js kullanıyorsanız, Uint8Array'i base64'e dönüştürmek için bu kodu kullanabilirsiniz.

var b64 = Buffer.from(u8).toString('base64');

4
Bu, performans açısından yukarıdaki el fonksiyonlarından daha iyi bir cevaptır.
Ben Liyanage

2
Harika! Teşekkürler. Şimdiye kadarki en iyi yanıt
Alan

18
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);
}

Çok büyük bir Uint8Array'ınız varsa bu işlevi kullanabilirsiniz. Bu, Javascript içindir, FileReader readAsArrayBuffer durumunda faydalı olabilir.


2
İlginç bir şekilde, Chrome'da bunu bir 300kb + arabellekte zamanladım ve onu bayt bayt yapmaktan çok biraz daha yavaş olduğu gibi parçalar halinde yaptığımı buldum. Bu beni şaşırttı.
Matt

@Matt ilginç. Bu arada, Chrome'un artık bu dönüşümü algılaması ve bunun için belirli bir optimizasyona sahip olması ve verilerin parçalanması, verimliliğini düşürebilir.
kanaka

2
Bu güvenli değil, değil mi? Parçamın sınırı çok baytlı UTF8 ile kodlanmış bir karakteri keserse, fromCharCode () sınırın her iki tarafındaki baytlardan mantıklı karakterler yaratamaz, değil mi?
Jens

2
@Jens String.fromCharCode.apply()yöntemleri UTF-8'i yeniden üretemez: UTF-8 karakterleri uzunluk olarak bir bayttan dört bayta kadar değişebilir, ancak String.fromCharCode.apply()UInt8 bölümlerindeki bir UInt8Array'ı inceler, bu nedenle hatalı olarak her karakterin tam olarak bir bayt uzunluğunda ve komşularından bağımsız olduğunu varsayar olanlar. UInt8Array girişinde kodlanan karakterlerin tümü ASCII (tek baytlık) aralığında olursa, şans eseri çalışacaktır, ancak tam UTF-8'i yeniden oluşturamaz. Bunun için TextDecoder veya benzer bir algoritmaya ihtiyacınız var.
Jamie Birch

1
@Jens, ikili veri dizisinde hangi çok baytlı UTF8 kodlu karakterler? Burada unicode dizeleriyle değil, utf-8 kod noktaları olarak değerlendirilmemesi gereken keyfi ikili verilerle uğraşıyoruz.
riv

0

İşte bunun için bir JS Fonksiyonu:

Bu işlev gereklidir çünkü Chrome, pushManager.subscribe içinde applicationServerKey için değer olarak base64 olarak kodlanmış bir dizeyi kabul etmez, ancak 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;
}

3
Bu, base64'ü Uint8Array'e dönüştürür. Ancak soru Uint8Array'in base64'e nasıl dönüştürüleceğini soruyor
Barry Michael Doyle

0

Saf JS - dizi orta adım yok (btoa yok)

Aşağıdaki çözümde dizeye dönüştürmeyi ihmal ediyorum. IDEA şu şekildedir:

  • 3 bayt (3 dizi öğesi) birleştirin ve 24 bit elde edin
  • 24 biti dört 6 bitlik sayıya bölün (0'dan 63'e kadar değerler alır)
  • bu sayıları base64 alfabesinde dizin olarak kullanın
  • köşe durumu: girdi bayt dizisi olduğunda, uzunluk 3'e bölünmez, sonra ekleyin =veya ==sonuca

Aşağıdaki çözüm 3 baytlık parçalar üzerinde çalışır, bu nedenle büyük diziler için iyidir. Base64'ü ikili diziye (olmadan atob) dönüştürmek için benzer çözüm BURADA


Kompaktlığı seviyorum, ancak ikili sayıyı temsil eden dizelere dönüştürmek ve sonra geri dönmek, kabul edilen çözümden çok daha yavaştır.
Garr Godfrey

0

Uint8 dizisini base64 kodlu dizeye dönüştürmek için aşağıdakileri kullanın

function arrayBufferToBase64(buffer) {
            var binary = '';
            var bytes = [].slice.call(new Uint8Array(buffer));
            bytes.forEach((b) => binary += String.fromCharCode(b));
            return window.btoa(binary);
        };


-3

Tek istediğiniz bir base64 kodlayıcının JS uygulamasıysa, verileri geri gönderebilmeniz için btoaişlevi deneyebilirsiniz .

b64enc = btoa(uint);

Btoa ile ilgili birkaç kısa not - bu standart değildir, bu nedenle tarayıcılar onu desteklemek zorunda değildir. Ancak çoğu tarayıcı bunu yapar. En azından büyük olanlar. atobtam tersi dönüşümdür.

Farklı bir uygulamaya ihtiyacınız varsa veya tarayıcının neden bahsettiğiniz hakkında hiçbir fikri olmadığı bir uç durum bulursanız, JS için bir base64 kodlayıcı aramak çok zor olmaz.

Sanırım şirketimin web sitesinde dolaşan 3 kişi var, bir nedenle ...


Teşekkürler, bunu daha önce denemedim.
Caio Keto

10
Birkaç not. btoa ve atob, aslında HTML5 standardizasyon sürecinin bir parçasıdır ve çoğu tarayıcı, bunları çoğunlukla aynı şekilde desteklemektedir. İkinci olarak, btoa ve atob yalnızca dizelerle çalışır. Btoa'yı Uint8Array üzerinde çalıştırmak, önce tamponu toString () kullanarak bir dizeye dönüştürür. Bu, "[nesne Uint8Array]" dizesiyle sonuçlanır. Muhtemelen amaçlanan bu değil.
kanaka

1
@CaioKeto, seçtiğiniz cevabı değiştirmeyi düşünebilirsiniz. Bu cevap doğru değil.
kanaka

-4

npm google-closure-kitaplığını yükle - kaydet

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.jsyazardım = AVMbY2Y konsoluna.


1
Yüksek bir -vecevaptan çok oylanmış bir cevabın kabul edilmesi komik +ve.
Vishnudev
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.