ArrayBuffer для base64 кодированной строки


203

Мне нужен эффективный (для чтения на родном) способ преобразования ArrayBufferстроки в base64, которую нужно использовать в составном сообщении.

Ответы:


222
function _arrayBufferToBase64( buffer ) {
    var binary = '';
    var bytes = new Uint8Array( buffer );
    var len = bytes.byteLength;
    for (var i = 0; i < len; i++) {
        binary += String.fromCharCode( bytes[ i ] );
    }
    return window.btoa( binary );
}

но не родные реализации быстрее, например https://gist.github.com/958841 см. http://jsperf.com/encoding-xhr-image-data/6


10
Я попробовал ненативную реализацию по ссылке, и потребовалось 1 минута и половина, чтобы преобразовать буфер размером 1 МБ, в то время как вышеприведенный код цикла занял только 1 секунду.
cshu

1
Мне нравится простота этого подхода, но вся эта конкатенация строк может быть дорогостоящей. Похоже, что создание массива символов и join()их окончание значительно быстрее в Firefox, IE и Safari (но в Chrome - намного медленнее): jsperf.com/tobase64-implementations
JLRishe

Я использую эту функцию для преобразования массива в буфер base64, но я не могу вернуть буфер массива. Я написал здесь функцию _base64ToArrayBuffer (): codeshare.io/PT4pb, но это выдает ошибку:Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.
bawejakunal

это не сработало это JSZip. я нахожу другой способ github.com/michael/github/issues/137
RouR

1
Я пытаюсь загрузить файл PDF 50 МБ, используя angualrjs и webapi2. Я использую вышеуказанный строчный код, после загрузки файла страница зависала и зависала. Ниже строки кода я был использован, но получил нулевое значение в методе webapi. "var base64String = btoa (String.fromCharCode.apply (null, new Uint8Array (arrayBuffer)));" пожалуйста, предложите любую идею ...
prabhakaran S

102

Это прекрасно работает для меня:

var base64String = btoa(String.fromCharCode.apply(null, new Uint8Array(arrayBuffer)));

В ES6 синтаксис немного проще:

let base64String = btoa(String.fromCharCode(...new Uint8Array(arrayBuffer)));

Как указано в комментариях, этот метод может привести к ошибке во время выполнения в некоторых браузерах, когда ArrayBufferон большой. Точный предел размера зависит от реализации в любом случае.


38
Мне нравится этот метод лучше для краткости, но получаю «ошибка превышения максимального размера стека вызовов». Приведенная выше техника петли помогает обойти это.
Джей

13
Я также получаю ошибку размера стека, поэтому я воспользовался ответом mobz, и он отлично работал.
Дэвид Джонс

12
Это не сработало для больших буферов. Небольшая модификация, чтобы заставить его работать:btoa([].reduce.call(new Uint8Array(bufferArray),function(p,c){return p+String.fromCharCode(c)},''))
laggingreflex

1
Я пытаюсь загрузить файл PDF 50 МБ, используя angualrjs и webapi2. Я использую вышеуказанный строчный код, после загрузки файла страница зависала и зависала. Ниже строки кода я был использован, но получил нулевое значение в методе webapi. "var base64String = btoa (String.fromCharCode.apply (null, new Uint8Array (arrayBuffer)));" пожалуйста, предложите любую идею ...
Prabhakaran S

2
@Kugel btoaбезопасен для символов в диапазоне кодов от 0 до 255, как и здесь (подумайте о 8 в Uint8Array).
GOTO 0

42

Для тех, кто любит это коротко, вот другое использование, Array.reduceкоторое не вызовет переполнение стека:

var base64 = btoa(
  new Uint8Array(arrayBuffer)
    .reduce((data, byte) => data + String.fromCharCode(byte), '')
);

4
Не уверен, что это действительно сексуально. В конце концов, вы создаете <amount of Bytes in the buffer>новые строки.
Неонит

Как насчет btoa(new Uint8Array(arraybuffer).reduce((data,byte)=>(data.push(String.fromCharCode(byte)),data),[]).join(''))?
Рой Тинкер

35

Существует еще один асинхронный способ использования Blob и FileReader.

Я не проверял производительность. Но это другой способ мышления.

function arrayBufferToBase64( buffer, callback ) {
    var blob = new Blob([buffer],{type:'application/octet-binary'});
    var reader = new FileReader();
    reader.onload = function(evt){
        var dataurl = evt.target.result;
        callback(dataurl.substr(dataurl.indexOf(',')+1));
    };
    reader.readAsDataURL(blob);
}

//example:
var buf = new Uint8Array([11,22,33]);
arrayBufferToBase64(buf, console.log.bind(console)); //"CxYh"

Используйте dataurl.split(',', 2)[1]вместо dataurl.substr(dataurl.indexOf(',')+1).
Картер Медлин

Кажется, это не сработает. Согласно w3c.github.io/FileAPI/#issue-f80bda5b readAsDataURL теоретически может вернуть процент закодированного dataURI (и, похоже, это действительно так в jsdom )
TS

@CarterMedlin Почему бы splitлучше, чем substring?
TS

Сплит короче. но dataurl может содержать одну или несколько запятых (,), split не является безопасным.
Cuixiping

12

Я использовал это и работает для меня.

function arrayBufferToBase64( buffer ) {
    var binary = '';
    var bytes = new Uint8Array( buffer );
    var len = bytes.byteLength;
    for (var i = 0; i < len; i++) {
        binary += String.fromCharCode( bytes[ i ] );
    }
    return window.btoa( binary );
}



function base64ToArrayBuffer(base64) {
    var binary_string =  window.atob(base64);
    var len = binary_string.length;
    var bytes = new Uint8Array( len );
    for (var i = 0; i < len; i++)        {
        bytes[i] = binary_string.charCodeAt(i);
    }
    return bytes.buffer;
}

Не безопасно. Смотрите @chemoish ответ
Кугель

11

Моя рекомендация для этого - НЕ использовать нативные btoaстратегии, так как они неправильно кодируют все ArrayBuffer

переписать DOMs atob () и btoa ()

Поскольку строки DOMString представляют собой строки с 16-битным кодированием, в большинстве браузеров вызов окна window.btoa для строки Unicode вызовет исключение «Символ вне диапазона», если символ превышает диапазон 8-битного символа в кодировке ASCII.

Хотя я никогда не сталкивался с этой точной ошибкой, я обнаружил, что многие из кодов, которые ArrayBufferя пытался кодировать, были закодированы неправильно.

Я бы использовал рекомендацию MDN или суть.


btoaне работает на String, но OP спрашивает ArrayBuffer.
TSH

1
Очень много это, так много фрагментов здесь, которые рекомендуют неправильную вещь! Я видел эту ошибку несколько раз, когда люди слепо используют atob и btoa.
Кугель

4
Все буферы массивов должны быть хорошо закодированы с использованием стратегий в других ответах, atob / btoa является проблемой только для текста, который содержит символы больше 0xFF (которые по определению не обрабатываются байтовыми массивами). Предупреждение MDN не применяется, потому что при использовании стратегии в других ответах вы гарантированно получите строку, состоящую только из символов ASCII, поскольку любое значение из Uint8Array гарантировано будет между 0 и 255, что означает, что String.fromCharCode гарантирован вернуть символ, который не находится вне диапазона.
Jamesernator

11
var blob = new Blob([arrayBuffer])

var reader = new FileReader();
reader.onload = function(event){
   var base64 =   event.target.result
};

reader.readAsDataURL(blob);

1
Пожалуйста, добавьте пояснения к своему ответу. Что означает этот код?
Оскар


1
это, безусловно, самый быстрый подход - в десятки раз быстрее, чем другие в моем ограниченном тестировании
Jitin

Я хотел бы найти это решение снова как 8 часов. мой день не был бы потрачен впустую; (спасибо
кайзер Шахид

7

Ниже приведены две простые функции для преобразования Uint8Array в Base64 String и обратно

arrayToBase64String(a) {
    return btoa(String.fromCharCode(...a));
}

base64StringToArray(s) {
    let asciiString = atob(s);
    return new Uint8Array([...asciiString].map(char => char.charCodeAt(0)));
}

1
Это запутанный ответ. Это не похоже на действительный JavaScript и является ли Uint8Array ArrayBuffer?
user1153660

@ user1153660 Добавьте functionключевое слово, и оно должно работать в современном браузере.
Йети

Потрясающие! btoa (String.fromCharCode (... а)); это самая короткая версия, которую я видел до сих пор для кодирования Uint8Array.
Николо

1
Это выглядит хорошо, но если массив слишком большой, он выдаст ошибку превышения максимального размера стека вызовов.
Павел Пштич

0

Вы можете получить нормальный массив с ArrayBufferпомощью Array.prototype.slice. Используйте функцию вроде Array.prototype.mapдля преобразования байтов в символы и joinих вместе в строку формата.

function arrayBufferToBase64(ab){

    var dView = new Uint8Array(ab);   //Get a byte view        

    var arr = Array.prototype.slice.call(dView); //Create a normal array        

    var arr1 = arr.map(function(item){        
      return String.fromCharCode(item);    //Convert
    });

    return window.btoa(arr1.join(''));   //Form a string

}

Этот метод работает быстрее, так как в нем нет конкатенации строк.


Не безопасно. Смотрите @chemoish ответ
Кугель

0

В OP не указана «Запущенная среда», но если вы используете Node.JS, существует очень простой способ сделать это.

Согласование с официальными документами Node.JS https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings

// This step is only necessary if you don't already have a Buffer Object
const buffer = Buffer.from(yourArrayBuffer);

const base64String = buffer.toString('base64');

Кроме того, если вы работаете под Angular, например, Buffer Class также будет доступен в среде браузера.


Ваш ответ относится только к NodeJS и не будет работать в браузере.
jvatic

1
@jvatic Понятно, что в ОП не была четко указана рабочая среда, поэтому мой ответ неверен, он только помечен Javascript. Поэтому я обновил свой ответ, чтобы сделать его более лаконичным. Я думаю, что это важный ответ, потому что я искал, как это сделать, и не смог найти лучший ответ на проблему.
Жоао Эдуардо Соареш и Сильва

-3

Со своей стороны, используя навигатор Chrome, мне пришлось использовать DataView () для чтения arrayBuffer

function _arrayBufferToBase64( tabU8A ) {
var binary = '';
let lecteur_de_donnees = new DataView(tabU8A);
var len = lecteur_de_donnees.byteLength;
var chaine = '';
var pos1;
for (var i = 0; i < len; i++) {
    binary += String.fromCharCode( lecteur_de_donnees.getUint8( i ) );
}
chaine = window.btoa( binary )
return chaine;}

-4
function _arrayBufferToBase64(uarr) {
    var strings = [], chunksize = 0xffff;
    var len = uarr.length;

    for (var i = 0; i * chunksize < len; i++){
        strings.push(String.fromCharCode.apply(null, uarr.subarray(i * chunksize, (i + 1) * chunksize)));
    }

    return strings.join("");
}

Это лучше, если вы используете JSZip для распаковки архива из строки

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.