Перетворити рядок base64 на ArrayBuffer


98

Мені потрібно перетворити рядок кодування base64 у ArrayBuffer. Рядки base64 вводяться користувачем, вони будуть скопійовані та вставлені з електронного листа, тому їх не буде, коли сторінка завантажується. Я хотів би зробити це в javascript, не здійснюючи виклику ajax на сервер, якщо це можливо.

Мені ці посилання були цікаві, але вони мені не допомогли:

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

мова йде про протилежне перетворення, від ArrayBuffer до base64, а не навпаки

http://jsperf.com/json-vs-base64/2

це виглядає добре, але я не можу зрозуміти, як використовувати код.

Чи існує простий (можливо, рідний) спосіб перетворення? Дякую

Відповіді:


147

Спробуйте це:

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

3
Поясніть, будь ласка, що насправді тут відбувається.
Говінда Сахаре,

4
Ну, це досить просто, спочатку ми декодуємо рядок base64 (atob), потім створюємо новий масив 8-бітових цілих чисел без знака з такою ж довжиною, як декодований рядок. Після цього ми повторюємо рядок і заповнюємо масив значенням Unicode кожного символу в рядку.
Горан. Це

2
З MDN: Base64 - це група подібних схем кодування двійкового тексту, які представляють двійкові дані у форматі рядка ASCII, перекладаючи їх у представлення radix-64. Набраний масив Uint8Array представляє масив 8-розрядних цілих чисел без знака, і ми працюємо з представленням даних ASCII (що також є 8-розрядною таблицею) ..
Goran.it

3
Це неправильно. Це дозволяє javascript інтерпретувати байти як рядок, що впливає на дані, які насправді є справжніми двійковими.
Томаш Зато - поновити Моніку

4
проблема полягає в тому, що а) не кожна послідовність байтів є дійсним юнікодом б) не кожен символ в юнікоді є одним байтом, тому bytes[i] = binary_string.charCodeAt(i);може бути помилковим
суміш

52

Використання TypedArray.from :

Uint8Array.from(atob(base64_string), c => c.charCodeAt(0))

Продуктивність для порівняння з цикловою версією відповіді Goran.it.


2
Хто любить такий лайнер, майте на увазі, що він Uint8Array.fromвсе ще мало сумісний з деякими браузерами.
IzumiSy

2
Будь ласка, не рекомендуйте atob або btoa: developer.mozilla.org/en-US/docs/Web/API/WindowBase64/…
Кугель

компілятор rails не може обробити цей рядок і не працює з ExecJS::RuntimeError: SyntaxError: Unexpected token: operator (>); (рейки 5)
Аваель Кросс

3
Це не буфер масиву. Це набраний масив. Ви отримуєте доступ до буфера масиву через .bufferвластивість того, з чого повертаєтьсяUint8Array
oligofren

4
@Saites, в цьому немає нічого поганого, atobабо btoaпросто потрібно надати їм дійсний ввід. atobпотрібен дійсний рядок base64, інакше це призведе до помилки. І btoaпотрібен дійсний байтовий рядок (також званий двійковим рядком), який є рядком, що містить символи в діапазоні 0-255. Якщо у вашому рядку є символи за межами цього діапазону, btoaз’явиться помилка.
GetFree

34

Відповідь Goran.it не працює через проблему юнікоду в javascript - https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding .

У підсумку я використав функцію, подану в блозі Даніеля Герреро: http://blog.danguer.com/2011/10/24/base64-binary-decoding-in-javascript/

Функція вказана за посиланням github: https://github.com/danguer/blog-examples/blob/master/js/base64-binary.js

Використовуйте ці рядки

var uintArray = Base64Binary.decode(base64_string);  
var byteArray = Base64Binary.decodeArrayBuffer(base64_string); 

1
Цей метод вдвічі швидший, ніж використання atob.
xiaoyu2er

4
Чи можете ви навести приклад, для якого це не спрацювало б? У статті йдеться про кодування довільних рядків, які можуть містити символи Unicode, але взагалі не стосуються atob.
рів

1
decodeArrayBufferповертає значення ArrayBuffer, яке завжди має розмір, що ділиться на 3, але я не розумію, це за дизайном чи помилка. Я запитаю в проекті github.
ceztko

@ceztko Це, мабуть, за (випадковим) дизайном. Алгоритм кодування base64 бере групи по 3 байти і перетворює їх у 4 символи. Метод декодування, ймовірно, виділяє ArrayBuffer, довжина якого - base64String.length / 4 * 3 байта, і ніколи не скорочує будь-які невикористані байти після закінчення.
Завжди Навчання

1
@AlwaysLearning, що означає, що це, ймовірно, помилка, оскільки залишки нульових байтів можуть пошкодити запланований вихідний вміст.
ceztko

21

Щойно знайшов base64-arraybuffer, невеликий пакет npm з неймовірно високим використанням, 5 млн завантажень минулого місяця (2017-08).

https://www.npmjs.com/package/base64-arraybuffer

Для тих, хто шукає щось найкраще стандартне рішення, це може бути саме воно.


9

Асинхронне рішення, краще, коли дані великі:

// base64 to buffer
function base64ToBufferAsync(base64) {
  var dataUrl = "data:application/octet-binary;base64," + base64;

  fetch(dataUrl)
    .then(res => res.arrayBuffer())
    .then(buffer => {
      console.log("base64 to buffer: " + new Uint8Array(buffer));
    })
}

// buffer to base64
function bufferToBase64Async( buffer ) {
    var blob = new Blob([buffer], {type:'application/octet-binary'});    
    console.log("buffer to blob:" + blob)

    var fileReader = new FileReader();
    fileReader.onload = function() {
      var dataUrl = fileReader.result;
      console.log("blob to dataUrl: " + dataUrl);

      var base64 = dataUrl.substr(dataUrl.indexOf(',')+1)      
      console.log("dataUrl to base64: " + base64);
    };
    fileReader.readAsDataURL(blob);
}

6

Javascript - прекрасне середовище для розробки, тому воно здається дивним, ніж не забезпечує вирішення цієї невеликої проблеми. Рішення, пропоновані в інших місцях на цій сторінці, потенційно повільні. Ось моє рішення. Він використовує вбудовану функціональність, яка декодує URL-адреси зображень і звукових даних base64.

var req = new XMLHttpRequest;
req.open('GET', "data:application/octet;base64," + base64Data);
req.responseType = 'arraybuffer';
req.onload = function fileLoaded(e)
{
   var byteArray = new Int8Array(e.target.response);
   // var shortArray = new Int16Array(e.target.response);
   // var unsignedShortArray = new Int16Array(e.target.response);
   // etc.
}
req.send();

Запит на відправлення не вдається, якщо базовий рядок 65 погано сформований.

Тип MIME (додаток / октет), мабуть, непотрібний.

Перевірено в хромі. Має працювати в інших браузерах.


1
Це було ідеальне рішення для мене, просте і чисте. Я швидко перевірив його у Firefox, IE 11, Edge і працював нормально!
cs-NET

не стосується початкового питання
Джеймс Ньютон

Я не впевнений, як це працює для вас в IE11, але я отримую Access Deniedпомилку, яка, здається, є обмеженням CORS.
Сергій

6

Для користувачів Node.js:

const myBuffer = Buffer.from(someBase64String, 'base64');

myBuffer буде типу Buffer, який є підкласом Uint8Array. На жаль, Uint8Array НЕ є ArrayBuffer, про що просив OP. Але, маніпулюючи ArrayBuffer, я майже завжди обгортаю його Uint8Array або чимось подібним, тому він повинен бути близьким до того, про що просять.


2

Pure JS - без рядка середнього кроку (без atob)

Я пишу наступну функцію, яка безпосередньо перетворює base64 (без перетворення в рядок на середньому кроці). ІДЕЯ

  • отримати 4 шматки символів base64
  • знайти індекс кожного символу в алфавіті base64
  • перетворення індексу в 6-бітове число (двійковий рядок)
  • об'єднати чотири 6-бітові числа, що дає 24-бітове число (зберігається як двійковий рядок)
  • розділіть 24-бітовий рядок на три 8-бітові і приховайте кожен до числа і збережіть їх у вихідному масиві
  • кутовий регістр: якщо вхідний рядок base64 закінчується одним / двома символами =, видаліть одне / два числа з вихідного масиву

Наведене нижче рішення дозволяє обробляти великі вхідні рядки base64. Подібна функція для перетворення байтів у base64 без btoa є ТУТ


так що відсутні відсутні "."?
Gillsoft AB

Тест у браузері, я не впевнений, що це очікуваний результат? "Пригода Аліси в країні чудес" (тобто останній персонаж - NaN)
Гілсофт А.Б.

1
@GillsoftAB дякую за цю інформацію - ти маєш рацію - я виправляю проблему
Kamil Kiełczewski

-3
const str = "dGhpcyBpcyBiYXNlNjQgc3RyaW5n"
const encoded = new TextEncoder().encode(str) // is Uint8Array
const buf = encoded.buffer // is ArrayBuffer

6
Зверніть увагу, що це не виконує ніякого декодування / кодування Base64. Він просто перетворює 6 байт "base64" у 6-елементний ArrayBuffer або Uint8Array.
дубек

2
@dubek це те, що просили.
Андрій Немченко
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.