Преобразование строки 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;
}
Goran.it
источник
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

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

jv-dev
источник
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 или чем-то подобным, поэтому он должен быть близок к тому, что запрашивается.

DoomGoober
источник
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 вот о чем спрашивали.
Андрей Немченко