Преобразовать строку base64 в ArrayBuffer
Мне нужно преобразовать строку кодирования base64 в ArrayBuffer. Строки base64 являются пользовательским вводом, они будут скопированы и вставлены из электронного письма, поэтому их не будет при загрузке страницы. Я хотел бы сделать это в javascript, не делая ajax-вызов на сервер, если это возможно.
Я нашел эти ссылки интересными, но они мне не помогли:
ArrayBuffer для base64 кодированной строки
речь идет об обратном преобразовании из ArrayBuffer в base64, а не наоборот
http://jsperf.com/json-vs-base64/2
это выглядит хорошо, но я не могу понять, как использовать код.
Есть ли простой (возможно, родной) способ сделать преобразование? Спасибо
15 ответов
Попробуй это:
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;
}
Используя TypedArray.from:
Uint8Array.from(atob(base64_string), c => c.charCodeAt(0))
Производительность сравнивать с циклической версией ответа Goran.it.
Для пользователей Node.js:
const myBuffer = Buffer.from(someBase64String, 'base64');
myBuffer будет иметь тип Buffer, который является подклассом Uint8Array. К сожалению, Uint8Array НЕ является ArrayBuffer, как просил OP. Но при манипулировании ArrayBuffer я почти всегда оборачиваю его Uint8Array или чем-то подобным, поэтому он должен быть близок к тому, что запрашивается.
Ответ 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);
Только что нашёл base64-arraybuffer, небольшой пакет npm с невероятно высокой загрузкой, 5 миллионов загрузок в прошлом месяце (2017-08).
https://www.npmjs.com/package/base64-arraybuffer
Для тех, кто ищет что-то лучшее стандартное решение, это может быть.
Асинхронное решение, лучше, когда данные большие:
// 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);
}
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();
Запрос на отправку завершается неудачно, если строка base 65 неправильно сформирована.
Тип MIME (application/octet), вероятно, не нужен.
Проверено в хром. Должно работать в других браузерах.
Чистый JS - без строкового промежуточного этапа (без атоба)
Я пишу следующую функцию, которая напрямую преобразует base64 (без преобразования в строку на промежуточном этапе). ИДЕЯ
- получить блок из 4 символов base64
- найти индекс каждого символа в алфавите base64
- преобразовать индекс в 6-битное число (двоичная строка)
- присоединиться к четырем 6-битным числам, что дает 24-битное число (хранится как двоичная строка)
- разделить 24-битную строку на три 8-битных и преобразовать каждую в число и сохранить их в выходном массиве
- угловой случай: если входная строка base64 заканчивается на один / два
=
char, удалить одно / два числа из выходного массива
Нижеприведенное решение позволяет обрабатывать большие входные строки base64. Аналогичная функция для преобразования байтов в base64 без btoa ЗДЕСЬ
function base64ToBytesArr(str) {
const abc = [..."ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"]; // base64 alphabet
let result = [];
for(let i=0; i<str.length/4; i++) {
let chunk = [...str.slice(4*i,4*i+4)]
let bin = chunk.map(x=> abc.indexOf(x).toString(2).padStart(6,0)).join('');
let bytes = bin.match(/.{1,8}/g).map(x=> +('0b'+x));
result.push(...bytes.slice(0,3 - (str[4*i+2]=="=") - (str[4*i+3]=="=")));
}
return result;
}
// --------
// TEST
// --------
let test = "Alice's Adventure in Wonderland.";
console.log('test string:', test.length, test);
let b64_btoa = btoa(test);
console.log('encoded string:', b64_btoa);
let decodedBytes = base64ToBytesArr(b64_btoa); // decode base64 to array of bytes
console.log('decoded bytes:', JSON.stringify(decodedBytes));
let decodedTest = decodedBytes.map(b => String.fromCharCode(b) ).join``;
console.log('Uint8Array', JSON.stringify(new Uint8Array(decodedBytes)));
console.log('decoded string:', decodedTest.length, decodedTest);
Я настоятельно рекомендую использовать пакет npm, правильно реализующий спецификацию base64.
Лучшее, что я знаю, это rfc4648
Проблема в том, что btoa и atob используют двоичные строки вместо Uint8Array, и попытки преобразовать в него и из него громоздки. Также для этого в npm есть много плохих пакетов. Я теряю много времени, прежде чем найду его.
Создатели этого конкретного пакета сделали простую вещь: они взяли спецификацию Base64 (который здесь , кстати) и реализовали его правильно с самого начала и до конца. (Включая в спецификацию другие форматы, которые также полезны, например, Base64-url, Base32 и т. Д.). Кажется, это не так уж много, но, очевидно, это было слишком много, чтобы просить кучу других библиотек.
Так что да, я знаю, что занимаюсь прозелитизмом, но если вы тоже хотите не терять время, просто используйте rfc4648.
new TextEncoder().encode("base64").buffer // is ArrayBuffer
new TextEncoder().encode("base64") // is Uint8Array
На самом деле, вы можете кодировать / декодировать любую строку, а не только строку base64.
MDN: https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder
Могу ли я использовать... https://caniuse.com/
Я использовал принятый ответ на этот вопрос для создания преобразований base64Url string <-> arrayBuffer в области данных base64Url, передаваемых через ASCII-cookie [atob, btoa являются base64 [с +/] <-> двоичной строкой js], поэтому я решил разместить код.
Многие из нас могут захотеть, чтобы и конверсии, и взаимодействие клиент-сервер могли использовать версию base64Url (хотя файл cookie может содержать символы +/, а также -_, если я правильно понимаю, только символы ",;\ и некоторые неверные символы из 128 ASCII). запрещены). Но URL-адрес не может содержать символ /, поэтому более широкое использование версии URL-адреса b64, которая, конечно, не поддерживается atob-btoa...
Видя другие комментарии, я хотел бы подчеркнуть, что мой вариант использования здесь — это передача данных base64Url через url/cookie и попытка использовать эти криптографические данные с js crypto api (2017), поэтому необходимо представление ArrayBuffer и преобразования b64u <-> arrBuff ... если буферы массива представляют что-то отличное от base64 (часть ascii), это преобразование не будет работать, поскольку atob, btoa ограничены ascii(128). Проверьте соответствующий конвертер, как показано ниже:
Версия buff -> b64u взята из твита Матиаса Байненса, спасибо и за него (тоже)! Он также написал кодировщик/декодер base64:https://github.com/mathiasbynens/base64 .
Исходя из java, это может помочь при попытке понять код, что java byte[] практически является js Int8Array (signed int), но мы используем здесь неподписанную версию Uint8Array, поскольку с ними работают преобразования js. Они оба 256-битные, поэтому теперь мы называем это byte[] в js...
Код из класса модуля, поэтому статический.
//utility
/**
* Array buffer to base64Url string
* - arrBuff->byte[]->biStr->b64->b64u
* @param arrayBuffer
* @returns {string}
* @private
*/
static _arrayBufferToBase64Url(arrayBuffer) {
console.log('base64Url from array buffer:', arrayBuffer);
let base64Url = window.btoa(String.fromCodePoint(...new Uint8Array(arrayBuffer)));
base64Url = base64Url.replaceAll('+', '-');
base64Url = base64Url.replaceAll('/', '_');
console.log('base64Url:', base64Url);
return base64Url;
}
/**
* Base64Url string to array buffer
* - b64u->b64->biStr->byte[]->arrBuff
* @param base64Url
* @returns {ArrayBufferLike}
* @private
*/
static _base64UrlToArrayBuffer(base64Url) {
console.log('array buffer from base64Url:', base64Url);
let base64 = base64Url.replaceAll('-', '+');
base64 = base64.replaceAll('_', '/');
const binaryString = window.atob(base64);
const length = binaryString.length;
const bytes = new Uint8Array(length);
for (let i = 0; i < length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
console.log('array buffer:', bytes.buffer);
return bytes.buffer;
}
сделал ArrayBuffer из base64:
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;
}
Я пытался использовать приведенный выше код, и он отлично работает.
(2023) Я прочитал все ответы и протестировал все решения:
- &
- &
- ручная реализация JavaScript
Ниже мой результат теста:
из буфера массива в base64:
btoa
не может обработать большой ввод (>1 МБ или около того), выдаст ошибку «Превышен максимальный размер стека вызовов».
очень хорошо.
Реализация Javascript работает, но в 10 раз медленнее, чем .
Итак, явный победительFileReader
, ниже приведен наиболее читаемый способ его написания:
async function arrayBufferToBase64(buffer) {
const file = new File([buffer], "temp.bin", {
type: "application/octet-stream",
});
const fileReader = new FileReader();
const dataUrl = await new Promise((resolve, reject) => {
fileReader.onload = () => resolve(fileReader.result);
fileReader.onerror = () => reject(fileReader.error);
fileReader.readAsDataURL(file);
});
return dataUrl.split("base64,")[1];
}
из base64 в буфер массива:
Реализация JavaScript работает лучше всего.
fetch
работает, но в 10 раз медленнее.
atob
работает, но еще медленнее.
Таким образом, реализация JavaScript является явным победителем. Вот что я использовал:
import { decode } from "base64-arraybuffer";
const buffer = decode(base64String);
Если вам не нравится устанавливать другой пакет, вы можете скопировать src-код , он занимает около 30 строк.
Решение без
Я видел много людей, жалующихся на использованиеatob
иbtoa
в ответах. Есть некоторые проблемы , которые следует учитывать при их использовании.
На странице MDN о Base64 есть решение без их использования . Ниже вы можете найти код для преобразования строки base64 в массив Uint8Array, скопированный из документации.
Обратите внимание, что приведенная ниже функция возвращает массив Uint8Array. Чтобы получить версию ArrayBuffer, вам просто нужно сделатьuintArray.buffer
.
function b64ToUint6(nChr) {
return nChr > 64 && nChr < 91
? nChr - 65
: nChr > 96 && nChr < 123
? nChr - 71
: nChr > 47 && nChr < 58
? nChr + 4
: nChr === 43
? 62
: nChr === 47
? 63
: 0;
}
function base64DecToArr(sBase64, nBlocksSize) {
const sB64Enc = sBase64.replace(/[^A-Za-z0-9+/]/g, "");
const nInLen = sB64Enc.length;
const nOutLen = nBlocksSize
? Math.ceil(((nInLen * 3 + 1) >> 2) / nBlocksSize) * nBlocksSize
: (nInLen * 3 + 1) >> 2;
const taBytes = new Uint8Array(nOutLen);
let nMod3;
let nMod4;
let nUint24 = 0;
let nOutIdx = 0;
for (let nInIdx = 0; nInIdx < nInLen; nInIdx++) {
nMod4 = nInIdx & 3;
nUint24 |= b64ToUint6(sB64Enc.charCodeAt(nInIdx)) << (6 * (3 - nMod4));
if (nMod4 === 3 || nInLen - nInIdx === 1) {
nMod3 = 0;
while (nMod3 < 3 && nOutIdx < nOutLen) {
taBytes[nOutIdx] = (nUint24 >>> ((16 >>> nMod3) & 24)) & 255;
nMod3++;
nOutIdx++;
}
nUint24 = 0;
}
}
return taBytes;
}
Если вас интересует обратная операция, ArrayBuffer в base64, вы можете найти, как это сделать, по той же ссылке.
Результатом atob является строка, разделенная запятой.
,
Более простой способ - преобразовать эту строку в строку массива json, а после этого проанализировать ее в byteArray ниже, код можно просто использовать для преобразования base64 в массив чисел
let byteArray = JSON.parse('['+atob(base64)+']');
let buffer = new Uint8Array(byteArray);