ArrayBuffer до кодованого рядка base64


203

Мені потрібен ефективний (читати рідний) спосіб перетворення ArrayBufferна рядок base64, який потрібно використовувати у багатопартійній публікації.

Відповіді:


221
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 () тут: codehare.io/PT4pb, але це дає мені помилку як:Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.
bawejakunal

це не працює цей JSZip. я знаходжу інший спосіб github.com/michael/github/isissue/137
RouR

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

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, новий Uint8Array (arrayBuffer)));"
підкажіть

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 теоретично міг би повернути відсоток закодованих данихURI (І, здається, це справді так у jsdom )
TS

@CarterMedlin Чому було splitб краще, ніж substring?
TS

спліт коротший. але dataurl може містити одну чи більше коми (,), розщеплення не є безпечним.
Куксипінг

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

Оскільки DOMStrings - це 16-бітові кодовані рядки, то в більшості браузерів, що викликають window.btoa в рядку Unicode, буде викликати виняток "Символ поза діапазоном", якщо символ перевищує діапазон 8-бітового символу, кодованого ASCII.

Хоча я ніколи не стикався з цією точною помилкою, я виявив, що багато хто з них ArrayBuffer , які я намагався кодувати, кодували неправильно.

Я б або застосував рекомендацію MDN, або суть.


btoaне працює на String, але ОП запитує ArrayBuffer.
тш

1
Дуже сильно це, так багато фрагментів тут, що рекомендують неправильну річ! Я не раз бачив цю помилку, коли люди наосліп використовують атоб та бтоа.
Кугель

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?
користувач1153660

@ user1153660 Додайте functionключове слово, і воно повинно працювати в сучасному браузері.
Йєті

Дивовижно! btoa (String.fromCharCode (... a)); це найкоротша версія, яку я бачив до цих пір для кодування Uint8Array.
Ніколо

1
Це виглядає добре, але якщо масив занадто великий, він викине максимальний розмір стека викликів, перевищений помилкою.
Paweł Psztyć

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

ОП не вказав Running Enviroment, але якщо ви використовуєте 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, клас буфера також буде доступний у середовищі браузера.


Ваша відповідь стосується лише NodeJS і не працюватиме в браузері.
jvatic

1
@jvatic Я бачу, в ОП чітко не було вказано Робоче середовище, тому моя відповідь є невірною, він лише позначив Javascript. Тому я оновив свою відповідь, щоб зробити її більш стислою. Я думаю, що це важлива відповідь, тому що я шукав, як це зробити, і не міг дійти до найкращої відповіді на проблему.
João Eduardo Soares e Silva

-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.