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

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

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.


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

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


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

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.


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.


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


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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.