Konwertuj ciąg base64 na ArrayBuffer

103

Muszę przekonwertować ciąg kodowania base64 na ArrayBuffer. Ciągi base64 są wprowadzane przez użytkownika, zostaną skopiowane i wklejone z wiadomości e-mail, więc nie ma ich tam, gdy strona jest ładowana. Chciałbym to zrobić w javascript bez wykonywania wywołania ajax do serwera, jeśli to możliwe.

Uważam, że te linki są interesujące, ale mi nie pomogły:

ArrayBuffer na ciąg zakodowany algorytmem Base64

chodzi o konwersję odwrotną, z ArrayBuffer na base64, a nie na odwrót

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

wygląda to dobrze, ale nie mogę wymyślić, jak używać kodu.

Czy istnieje łatwy (może natywny) sposób przeprowadzenia konwersji? dzięki

Tony
źródło

Odpowiedzi:

152

Spróbuj tego:

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
źródło
4
Proszę, wyjaśnij mi, co się tu naprawdę dzieje.
Govi S
4
Cóż, jest to całkiem proste, najpierw dekodujemy ciąg base64 (atob), a następnie tworzymy nową tablicę 8-bitowych liczb całkowitych bez znaku o tej samej długości co zdekodowany ciąg. Następnie wykonujemy iterację ciągu i zapełniamy tablicę wartością Unicode każdego znaku w ciągu.
Goran.it
2
Z MDN: Base64 to grupa podobnych schematów kodowania binarnego na tekst, które reprezentują dane binarne w formacie ASCII, tłumacząc je na reprezentację radix-64. Tablica typu Uint8Array reprezentuje tablicę 8-bitowych liczb całkowitych bez znaku i pracujemy z reprezentacją danych w formacie ASCII (która jest również tabelą 8-bitową) ..
Goran.it
3
To nie jest poprawne. Pozwala javascript interpretować bajty jako ciąg znaków, co wpływa na dane, które w rzeczywistości są binarne.
Tomáš Zato - Przywróć Monikę
4
problem polega na tym, że a) nie każda sekwencja bajtów jest poprawna w Unicode b) nie każdy znak w Unicode to jeden bajt, więc bytes[i] = binary_string.charCodeAt(i);może być źle
mieszanka
52

Korzystanie z TypedArray.from :

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

Wydajność do porównania z wersją pętli for odpowiedzi Goran.it.

ofavre
źródło
2
Kto lubi tego rodzaju jedną linijkę, pamiętaj, że Uint8Array.fromnadal ma niewielką zgodność z niektórymi przeglądarkami.
IzumiSy
Kompilator rails nie może obsłużyć tego ciągu i kończy się niepowodzeniem ExecJS::RuntimeError: SyntaxError: Unexpected token: operator (>); (tory 5)
Avael Kross
3
To nie jest bufor tablicy. To jest wpisana tablica. Dostęp do bufora tablicy uzyskuje się poprzez .bufferwłaściwość tego, co jest zwracane zUint8Array
oligofren,
4
@Saites, Nie ma nic złego w atoblub btoa, po prostu musisz podać prawidłowe dane wejściowe. atobwymaga prawidłowego ciągu base64, w przeciwnym razie zgłosi błąd. I btoapotrzebuje prawidłowego ciągu bajtów (zwanego również ciągiem binarnym), który jest ciągiem zawierającym znaki z zakresu 0-255. Jeśli twój ciąg ma znaki spoza tego zakresu, btoazwróci błąd.
GetFree
34

Odpowiedź Goran.it nie działa z powodu problemu z Unicode w javascript - https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/Base64_encoding_and_decoding .

Skończyło się na tym, że skorzystałem z funkcji podanej na blogu Daniela Guerrero: http://blog.danguer.com/2011/10/24/base64-binary-decoding-in-javascript/

Funkcja jest wymieniona na linku github: https://github.com/danguer/blog-examples/blob/master/js/base64-binary.js

Użyj tych linii

var uintArray = Base64Binary.decode(base64_string);  
var byteArray = Base64Binary.decodeArrayBuffer(base64_string); 
Yaan
źródło
1
Ta metoda jest 2x szybsza niż użycie atob.
xiaoyu2er
4
Czy możesz podać przykład, na którym to nie zadziała? Artykuł mówi o kodowaniu dowolnych ciągów, które mogą zawierać znaki Unicode, ale w ogóle ich nie dotyczą atob.
riv
1
decodeArrayBufferzwraca wartość, ArrayBufferktóra ma rozmiar zawsze podzielny przez 3, czego nie rozumiem, jeśli jest to zgodne z projektem lub błędem. Zapytam w projekcie github.
ceztko
@ceztko To prawdopodobnie (przypadkowe) zaprojektowanie. Algorytm kodowania base64 przyjmuje grupy po 3 bajty i zamienia je na 4 znaki. Metoda dekodowania prawdopodobnie przydziela ArrayBuffer o długości base64String.length / 4 * 3 bajty i nigdy nie obcina nieużywanych bajtów po zakończeniu.
AlwaysLearning
1
@AlwaysLearning, co oznacza, że ​​prawdopodobnie jest uszkodzony, ponieważ pozostałe zero bajtów może uszkodzić zamierzoną zawartość wyjściową.
ceztko
22

Właśnie znalazłem base64-arraybuffer, mały pakiet npm o niewiarygodnie wysokim wykorzystaniu, 5 milionów pobrań w zeszłym miesiącu (2017-08).

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

Dla każdego, kto szuka czegoś w najlepszym standardowym rozwiązaniu, może to być to.

jv-dev
źródło
9

Rozwiązanie asynchroniczne , lepiej, gdy dane są duże:

// 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);
}
张浩然
źródło
7

Dla użytkowników Node.js:

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

myBuffer będzie typu Buffer, który jest podklasą Uint8Array. Niestety, Uint8Array NIE jest ArrayBuffer, o który prosił OP. Ale podczas manipulowania ArrayBuffer prawie zawsze zawijam go za pomocą Uint8Array lub czegoś podobnego, więc powinien być blisko tego, o co jest proszony.

DoomGoober
źródło
6

Javascript to dobre środowisko programistyczne, więc wydaje się dziwne, ponieważ nie zapewnia rozwiązania tego małego problemu. Rozwiązania oferowane w innych miejscach na tej stronie są potencjalnie wolne. Oto moje rozwiązanie. Wykorzystuje wbudowaną funkcjonalność, która dekoduje adresy URL obrazu i dźwięku 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();

Żądanie wysłania kończy się niepowodzeniem, jeśli łańcuch podstawowy 65 jest nieprawidłowo sformułowany.

Typ MIME (aplikacja / oktet) jest prawdopodobnie niepotrzebny.

Testowany w chromie. Powinien działać w innych przeglądarkach.

koniczyna dinozaurów
źródło
1
To było dla mnie idealne rozwiązanie, proste i czyste. Szybko przetestowałem to w Firefoksie, IE 11, Edge i działało dobrze!
cs-NET
nie odnosi się do pierwotnego pytania
James Newton
Nie jestem pewien, jak to działa w IE11, ale pojawia się Access Deniedbłąd, który wydaje się być ograniczeniem CORS.
Sergiu,
2

Czysty JS - bez łańcucha po środku (bez atob)

Piszę następującą funkcję, która konwertuje base64 w sposób bezpośredni (bez konwersji na string w środku). POMYSŁ

  • zdobądź 4 fragmenty znaków base64
  • znajdź indeks każdego znaku w alfabecie base64
  • zamień indeks na liczbę 6-bitową (ciąg binarny)
  • połącz cztery 6-bitowe liczby, które dają 24-bitową liczbę (przechowywaną jako ciąg binarny)
  • podziel ciąg 24-bitowy na trzy 8-bitowe i zamień każdy na numer i zapisz je w tablicy wyjściowej
  • przypadek narożny: jeśli wejściowy ciąg base64 kończy się jednym / dwoma =znakami, usuń jedną / dwie liczby z tablicy wyjściowej

Poniższe rozwiązanie pozwala na przetwarzanie dużych łańcuchów wejściowych base64. Podobna funkcja do konwersji bajtów na base64 bez btoa jest TUTAJ

Kamil Kiełczewski
źródło
więc nie brakuje „.”?
Gillsoft AB
Przetestuj w przeglądarce, nie jestem pewien, czy to jest oczekiwany wynik? „Alice's Adventure in Wonderland” (czyli ostatnia postać to NaN)
Gillsoft AB
1
@GillsoftAB dziękuję za te informacje - masz rację - naprawiam problem
Kamil Kiełczewski
-4
const str = "dGhpcyBpcyBiYXNlNjQgc3RyaW5n"
const encoded = new TextEncoder().encode(str) // is Uint8Array
const buf = encoded.buffer // is ArrayBuffer
Andrii Nemchenko
źródło
6
Zauważ, że to nie wykonuje żadnego dekodowania / kodowania Base64. Po prostu zamienia 6 bajtów "base64" w 6-elementowy ArrayBuffer lub Uint8Array.
dubek
2
@dubek o to pytano.
Andrii Nemchenko