uint8 배열을 base64 인코딩 문자열로 변환하는 방법은 무엇입니까?


답변:


15

이미 제안 된 모든 솔루션에는 심각한 문제가 있습니다. 일부 솔루션은 대형 배열에서 작동하지 않고 일부는 잘못된 출력을 제공하며 일부는 중간 문자열에 멀티 바이트 문자가 포함 된 경우 btoa 호출에 오류가 발생하고 일부는 필요한 것보다 더 많은 메모리를 소비합니다.

그래서 입력에 관계없이 작동하는 직접 변환 기능을 구현했습니다. 내 컴퓨터에서 초당 약 5 백만 바이트를 변환합니다.

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


base64abc를 문자열 배열로 사용하는 것이 단순히 문자열로 만드는 것보다 빠릅니까? "ABCDEFG..."?
Garr Godfrey

161

데이터에 다중 바이트 시퀀스 (일반 ASCII 시퀀스가 ​​아님)가 있고 브라우저에 TextDecoder 가있는 경우이를 사용하여 데이터를 디코딩해야합니다 (TextDecoder에 필요한 인코딩 지정).

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

TextDecoder (현재 IE 및 Edge) 가없는 브라우저 를 지원해야하는 경우 가장 좋은 방법은 TextDecoder polyfill 을 사용하는 입니다.

데이터에 일반 ASCII (멀티 바이트 유니 코드 / 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));

4
이것은 Firefox에서 저에게 효과적이지만 Chrome은 "Uncaught RangeError : Maximum call stack size exceeded"(btoa 수행)로 인해 질식합니다.
Michael Paulukonis 2014

3
@MichaelPaulukonis 내 생각 엔 실제로 스택 크기를 초과하는 String.fromCharCode.apply입니다. Uint8Array가 매우 큰 경우 apply를 사용하는 대신 문자열을 반복적으로 빌드해야 할 것입니다. apply () 호출은 배열의 모든 요소를 ​​fromCharCode에 매개 변수로 전달하므로 배열의 길이가 128000 바이트이면 스택을 날려 버릴 가능성이있는 128000 개의 매개 변수를 사용하여 함수 호출을 시도 할 것입니다.
kanaka

4
감사. 내가 필요로하는 것은btoa(String.fromCharCode.apply(null, myArray))
Glen Little

29
바이트 배열이 유효한 유니 코드가 아닌 경우 작동하지 않습니다.
Melab 2017-11-16

11
base64 문자열 또는 Uint8Array. 128..255 범위의 바이트가 TextDecoder있으면 Uint8Array텍스트 디코더가이를 유니 코드 문자로 잘못 변환하여 base64 변환기를 손상 시키므로 여기서 사용하는 것은 절대적으로 잘못된 것 입니다.
riv

26

매우 간단한 솔루션과 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));

4
가장 깨끗한 솔루션!
realappie

완벽한 솔루션
Haris ur Rehman

2
대용량 데이터 (예 : 이미지)에서 실패합니다.RangeError: Maximum call stack size exceeded
Maxim Khokhryakov

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

Uint8Array가 매우 큰 경우이 함수를 사용할 수 있습니다. 이것은 Javascript 용이며 FileReader readAsArrayBuffer의 경우 유용 할 수 있습니다.


2
흥미롭게도 Chrome에서 나는 이것을 300kb + 버퍼로 시간을 정했고, 당신이 바이트 단위로하는 것보다 조금 더 느리다는 것을 알았습니다. 이것은 나를 놀라게했다.
Matt

@Matt 흥미로운. 그 동안 Chrome이 이제이 변환을 감지하고 이에 대한 특정 최적화를 수행하고 데이터를 청킹하면 효율성이 감소 할 수 있습니다.
kanaka

2
이건 안전하지 않지? 내 청크의 경계가 다중 바이트 UTF8 인코딩 문자를 잘라 내면 fromCharCode () 가 경계 양쪽의 바이트에서 합리적인 문자를 만들 수 없습니까?
Jens

2
@Jens String.fromCharCode.apply()메서드는 UTF-8을 재현 할 수 없습니다. UTF-8 문자의 길이는 1 바이트에서 4 바이트까지 다양 할 수 있지만 String.fromCharCode.apply()UInt8의 세그먼트에서 UInt8Array를 검사하므로 각 문자의 길이가 정확히 1 바이트이고 인접 문자와 독립적이라고 잘못 가정합니다. 하나. 입력 UInt8Array에 인코딩 된 문자가 모두 ASCII (단일 바이트) 범위에 있으면 우연히 작동하지만 전체 UTF-8을 재현 할 수는 없습니다. TextDecoder 또는 이와 유사한 알고리즘이 필요합니다.
Jamie Birch

1
@Jens 바이너리 데이터 배열에서 어떤 다중 바이트 UTF8 인코딩 문자? 여기서는 유니 코드 문자열을 다루지 않고 utf-8 코드 포인트로 취급해서는 안되는 임의의 이진 데이터를 다루고 있습니다.
riv

15

Node.js를 사용하는 경우이 코드를 사용하여 Uint8Array를 base64로 변환 할 수 있습니다.

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

4
이것은 성능 측면에서 위의 손으로 굴린 기능보다 더 나은 대답입니다.
Ben Liyanage

2
대박! 감사. 최고의 답변
Alan

2
완전한!! 이것이 허용되는 대답이 될 것입니다!
m4l490n

1
정답입니다
Pablo Yabo

0

여기에 JS 함수가 있습니다.

Chrome은 아직 pushManager.subscribe의 applicationServerKey 값으로 base64로 인코딩 된 문자열을 허용하지 않기 때문에이 함수가 필요합니다. 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
이것은 base64를 Uint8Array로 변환합니다. 하지만 문제는 64 기수로 Uint8Array를 변환하는 방법을 묻습니다
배리 마이클 도일에게

0

순수 JS-문자열 중간 단계 없음 (btoa 없음)

아래 솔루션에서는 문자열로의 변환을 생략합니다. IDEA는 다음과 같습니다.

  • 3 바이트 (3 개의 배열 요소)를 결합하면 24 비트가됩니다.
  • 24 비트를 4 개의 6 비트 숫자로 분할 (0에서 63까지의 값 사용)
  • 그 숫자를 base64 알파벳의 색인으로 사용하십시오.
  • 코너 케이스 : 입력 바이트 배열의 길이가 3으로 나뉘 지 않은 경우 추가 =또는 ==결과

아래 솔루션은 3 바이트 청크에서 작동하므로 큰 배열에 적합합니다. base64를 이진 배열로 변환하는 유사한 솔루션 (없음 atob)이 여기 있습니다.


나는 간결함을 좋아하지만 이진수를 나타내는 문자열로 변환 한 다음 다시 돌아 오는 것은 허용되는 솔루션보다 훨씬 느립니다.
Garr Godfrey

0

다음을 사용하여 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);
        };


-1

이에 대한 매우 좋은 접근 방식은 Mozilla 개발자 네트워크 웹 사이트에 나와 있습니다 .

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 "☸☹☺☻☼☾☿"


-3

원하는 것은 base64 인코더의 JS 구현뿐이므로 데이터를 다시 보낼 수 있습니다 btoa. 함수를 사용해 볼 수 있습니다 .

b64enc = btoa(uint);

btoa에 대한 몇 가지 간단한 참고 사항-비표준이므로 브라우저가이를 지원하도록 강요하지 않습니다. 그러나 대부분의 브라우저는 그렇습니다. 적어도 큰 것. atob반대의 변환입니다.

다른 구현이 필요하거나 브라우저가 당신이 무슨 말을하는지 알지 못하는 엣지 케이스를 발견한다면 JS 용 base64 인코더를 찾는 것은 그리 어렵지 않을 것입니다.

제 회사 웹 사이트에 왠지 3 개가 걸려있는 것 같아요 ...


고마워요, 전에 해보지 않았어요.
Caio Keto

10
몇 가지 메모. btoa 및 atob은 실제로 HTML5 표준화 프로세스의 일부이며 대부분의 브라우저는 이미 거의 동일한 방식으로 지원합니다. 둘째, btoa 및 atob은 문자열에서만 작동합니다. Uint8Array에서 btoa를 실행하면 먼저 toString ()을 사용하여 버퍼를 문자열로 변환합니다. 결과적으로 "[object Uint8Array]"문자열이 생성됩니다. 그것은 아마도 의도 된 것이 아닐 것입니다.
kanaka

1
@CaioKeto 선택한 답변을 변경하는 것이 좋습니다. 이 대답은 정확하지 않습니다.
kanaka

-4

npm install 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작성합니다 = AVMbY2Y을 콘솔에.


1
-ve높은 답변이 아닌 투표 된 답변이 받아 들여지는 것은 재밌습니다 +ve.
Vishnudev
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.