Преобразование строки base64 в ArrayBuffer


105

Мне нужно преобразовать строку кодирования base64 в ArrayBuffer. Строки base64 вводятся пользователем, они будут скопированы и вставлены из электронного письма, поэтому их нет при загрузке страницы. Я хотел бы сделать это в javascript, если это возможно, без вызова сервера ajax.

Эти ссылки показались мне интересными, но они мне не помогли:

ArrayBuffer в строку в кодировке base64

речь идет об обратном преобразовании из ArrayBuffer в base64, а не наоборот

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

это выглядит хорошо, но я не могу понять, как использовать код.

Есть ли простой (возможно, собственный) способ выполнить преобразование? Благодарность

Ответы:


153

Попробуй это:

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

4
Пожалуйста, объясните мне, что здесь происходит на самом деле.
Govi S

4
Что ж, это довольно просто, сначала мы декодируем строку base64 (atob), затем мы создаем новый массив 8-битных целых чисел без знака той же длины, что и декодированная строка. После этого мы перебираем строку и заполняем массив значением Unicode для каждого символа в строке.
Goran.it

2
Из MDN: Base64 - это группа аналогичных схем кодирования двоичного кода в текст, которые представляют двоичные данные в строковом формате ASCII, переводя их в представление radix-64. Типизированный массив Uint8Array представляет собой массив 8-битных целых чисел без знака, и мы работаем с ASCII-представлением данных (которое также является 8-битной таблицей) ..
Goran.it

3
Это не так. Это позволяет javascript интерпретировать байты как строку, что влияет на данные, которые на самом деле являются истинно двоичными.
Tomáš Zato - Reinstate Monica

4
проблема в том, что а) не каждая последовательность байтов допустима в Юникоде б) не каждый символ в Юникоде является одним байтом, поэтому bytes[i] = binary_string.charCodeAt(i);может ошибаться
смесь

54

Использование TypedArray.from :

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

Производительность сравнивается с версией ответа Goran.it для цикла for.


2
Тем, кому нравится такой лайнер, имейте в виду, что он по- Uint8Array.fromпрежнему мало совместим с некоторыми браузерами.
IzumiSy

2
Пожалуйста, не рекомендуйте atob или btoa: developer.mozilla.org/en-US/docs/Web/API/WindowBase64/…
Kugel

Компилятор 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
Этот метод в 2 раза быстрее, чем при использовании atob.
xiaoyu2er

4
Вы можете привести пример, для которого это не сработает? В статье говорится о кодировании произвольных строк, которые могут содержать символы Unicode, но не относятся к ним atobвообще.
riv

1
decodeArrayBufferвозвращает ArrayBufferразмер, который всегда делится на 3, что я не понимаю, является ли это дизайном или ошибкой. Спрошу в проекте github.
ceztko

@ceztko Это, вероятно, (случайное) намерение. Алгоритм кодирования base64 берет группы по 3 байта и превращает их в 4 символа. Метод декодирования, вероятно, выделяет ArrayBuffer, длина которого составляет base64String.length / 4 * 3 байта, и никогда не обрезает неиспользуемые байты по завершении.
AlwaysLearning

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

23

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

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

Это может быть оно для всех, кто ищет лучшее стандартное решение.


10

Асинхронное решение, лучше, когда данные большие:

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

7

Для пользователей Node.js:

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

myBuffer будет иметь тип Buffer, который является подклассом Uint8Array. К сожалению, Uint8Array НЕ является ArrayBuffer, как просил OP. Но при манипулировании ArrayBuffer я почти всегда оборачиваю его Uint8Array или чем-то подобным, поэтому он должен быть близок к тому, что запрашивается.


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 Uint8Array(e.target.response);
   // var shortArray = new Int16Array(e.target.response);
   // var unsignedShortArray = new Int16Array(e.target.response);
   // etc.
}
req.send();

Запрос на отправку не выполняется, если строка base 64 неправильно сформирована.

Тип mime (приложение / октет), вероятно, не нужен.

Проверено в хроме. Должен работать в других браузерах.


1
Для меня это было идеальное решение, простое и чистое. Я быстро протестировал его в Firefox, IE 11, Edge и работал нормально!
cs-NET

Я не уверен, как это работает для вас в IE11, но получаю сообщение Access Deniedоб ошибке, которое, похоже, является ограничением CORS.
Серджиу

2

Чистый JS - без строкового промежуточного этапа (без атоба)

Я пишу следующую функцию, которая напрямую преобразует base64 (без преобразования в строку на промежуточном этапе). ИДЕЯ

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

Нижеприведенное решение позволяет обрабатывать большие входные строки base64. Аналогичная функция для преобразования байтов в base64 без btoa ЗДЕСЬ


так что не пропало "."?
Gillsoft AB,

Протестируйте в браузере, не уверен, что это ожидаемый результат? «Приключение Алисы в стране чудес» (т.е. последний символ - NaN)
Gillsoft AB

1
@GillsoftAB спасибо за эту информацию - вы правы - я
исправил

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

6
Обратите внимание, что при этом не выполняется декодирование / кодирование Base64. Он просто превращает 6 байтов base64 в 6-элементный ArrayBuffer или Uint8Array.
dubek

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