Kodowanie i dekodowanie Base64 w JavaScript po stronie klienta

Odpowiedzi:

213

Niektóre przeglądarki, takie jak Firefox, Chrome, Safari, Opera i IE10 +, mogą obsługiwać Base64 natywnie. Spójrz na to pytanie Stackoverflow . To używa btoa()i atob()funkcje .

W przypadku JavaScript po stronie serwera (węzeł) można użyć Buffers do dekodowania.

Jeśli szukasz rozwiązania dla różnych przeglądarek, istnieją biblioteki takie jak CryptoJS lub kod:

http://ntt.cc/2008/01/19/base64-encoder-decoder-with-javascript.html

W tym drugim przypadku musisz dokładnie przetestować tę funkcję pod kątem zgodności z różnymi przeglądarkami. Błąd został już zgłoszony .

Ranhiru Jude Cooray
źródło
1
Użyłem tej metody do zakodowania SVG w base64 za pomocą schematu URI daty. Niespodzianka: ta funkcja urlencoduje każdą postać, więc otrzymuję ten zniekształcony kod XML w celu:% 3C% 3Fxml% 20wersja% 3D% 271,0% 27% 20% 3F% 3E% 3Csvg% 20xmlns% 3D% 27http% ...
Dereckson,
21
Node.js może wykonać Base64 natywnie: new Buffer('Hello, world!').toString('base64'); new Buffer('SGVsbG8sIHdvcmxkIQ==', 'base64').toString('ascii'); (źródło)
nyuszika7h
A jeśli chcę to zrobić z bezpiecznym adresem URL?
anddero
1
w węźle 5+ użyj Buffer.from, ponieważ new Buffer(string)jest przestarzałe. Buffer.from(jwt.split('.')[1], 'base64').toString()
Ray Foss,
63

W przeglądarkach opartych na Gecko / WebKit (Firefox, Chrome i Safari) oraz Opera można używać btoa () i atob () .

Oryginalna odpowiedź: jak możesz zakodować ciąg znaków w Base64 w JavaScript?

nyuszika7h
źródło
To jest ratowanie życia. Użyłem kilku różnych implementacji, aby zdekodować bardzo duże ciągi kodowane w standardzie base64, a wynik zawsze był zły. atob () działa świetnie!
b2238488,
15
Mały nitpick: Opera nie jest oparta na Gecko ani Webkit, wykorzystuje własny silnik renderujący o nazwie Presto.
Peter Olson,
Wow, dzięki za to. Nie wiedziałem, że w tych przeglądarkach jest natywny koder base64!
Rob Porter,
5
@PeterOlson Już nie :)
Mustafa
1
Zdaję sobie sprawę, że jest to stary post, ale jeśli chodzi o troskę @ b2238488, możesz podzielić ciąg base64, aby długość każdego tokenu była wielokrotnością 4, i zdekodować je osobno. Wynik będzie taki sam, jak dekodowanie całego ciągu naraz.
nyuszika7h,
56

Internet Explorer 10+

// Define the string
var string = 'Hello World!';

// Encode the String
var encodedString = btoa(string);
console.log(encodedString); // Outputs: "SGVsbG8gV29ybGQh"

// Decode the String
var decodedString = atob(encodedString);
console.log(decodedString); // Outputs: "Hello World!"

Przeglądarka

Ponownie napisane i zmodularyzowane biblioteki / moduły kodowania i dekodowania JavaScript UTF-8 i Base64 dla AMD, CommonJS, Nodejs i przeglądarek. Kompatybilny z wieloma przeglądarkami.


z Node.js

Oto jak kodować normalny tekst do base64 w Node.js:

//Buffer() requires a number, array or string as the first parameter, and an optional encoding type as the second parameter. 
// Default is utf8, possible encoding types are ascii, utf8, ucs2, base64, binary, and hex
var b = new Buffer('JavaScript');
// If we don't use toString(), JavaScript assumes we want to convert the object to utf8.
// We can make it convert to other formats by passing the encoding type to toString().
var s = b.toString('base64');

A oto jak dekodujesz ciągi zakodowane w base64:

var b = new Buffer('SmF2YVNjcmlwdA==', 'base64')
var s = b.toString();

z Dojo.js

Aby zakodować tablicę bajtów przy użyciu dojox.encoding.base64:

var str = dojox.encoding.base64.encode(myByteArray);

Aby zdekodować ciąg zakodowany w standardzie base64:

var bytes = dojox.encoding.base64.decode(str)

altana zainstalować kątowy-base64

<script src="bower_components/angular-base64/angular-base64.js"></script>

angular
    .module('myApp', ['base64'])
    .controller('myController', [

    '$base64', '$scope', 
    function($base64, $scope) {
    
        $scope.encoded = $base64.encode('a string');
        $scope.decoded = $base64.decode('YSBzdHJpbmc=');
}]);

Ale jak?

Jeśli chcesz dowiedzieć się więcej o tym, jak ogólnie kodowane jest base64, aw szczególności w JavaScript, polecam ten artykuł: Informatyka w JavaScript: kodowanie Base64

Davidcondrey
źródło
1
FYI: Wersja cross-browser ma kilka nieprzyjemnych przecieki z c2a prawdopodobnie c1i c3tak nie będzie działać z "use strict"jak zdefiniowano powyżej.
Campbeln,
41

Oto zaostrzona wersja postu Snajpera. Zakłada dobrze uformowany ciąg base64 bez powrotu karetki. Ta wersja eliminuje kilka pętli, dodaje &0xffpoprawkę z Jarosławia, eliminuje końcowe null oraz odrobinę golfa kodu.

decodeBase64 = function(s) {
    var e={},i,b=0,c,x,l=0,a,r='',w=String.fromCharCode,L=s.length;
    var A="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    for(i=0;i<64;i++){e[A.charAt(i)]=i;}
    for(x=0;x<L;x++){
        c=e[s.charAt(x)];b=(b<<6)+c;l+=6;
        while(l>=8){((a=(b>>>(l-=8))&0xff)||(x<(L-2)))&&(r+=w(a));}
    }
    return r;
};
broc.seib
źródło
5
Jeszcze mniej bajtów; DdecodeBase64=function(f){var g={},b=65,d=0,a,c=0,h,e="",k=String.fromCharCode,l=f.length;for(a="";91>b;)a+=k(b++);a+=a.toLowerCase()+"0123456789+/";for(b=0;64>b;b++)g[a.charAt(b)]=b;for(a=0;a<l;a++)for(b=g[f.charAt(a)],d=(d<<6)+b,c+=6;8<=c;)((h=d>>>(c-=8)&255)||a<l-2)&&(e+=k(h));return e};
Der Hochstapler
Tak, ale działa tylko z ASCII. Na przykład postacie Cyr są zniekształcone.
Martin Kovachev
@MartinKovachev czy możesz opublikować nowy komentarz z przykładowym tekstem z znakami Cyr i odpowiednim kodowaniem base64? Być może możemy naprawić kod, aby go dostosować.
broc.seib,
Tutaj: coś takiego: тестова фраза
Martin Kovachev
2
@OliverSalzburg Jeszcze mniej wygeneruj tabelę kodów :):var g={},k=String.fromCharCode,i;for(i=0;i<64;)g[k(i>61?(i&1)*4|43:i+[65,71,-4][i/26&3])]=i++;
Mike
34

Krótka i szybka funkcja dekodowania JavaScript Base64 bez Failsafe:

function decode_base64 (s)
{
    var e = {}, i, k, v = [], r = '', w = String.fromCharCode;
    var n = [[65, 91], [97, 123], [48, 58], [43, 44], [47, 48]];

    for (z in n)
    {
        for (i = n[z][0]; i < n[z][1]; i++)
        {
            v.push(w(i));
        }
    }
    for (i = 0; i < 64; i++)
    {
        e[v[i]] = i;
    }

    for (i = 0; i < s.length; i+=72)
    {
        var b = 0, c, x, l = 0, o = s.substring(i, i+72);
        for (x = 0; x < o.length; x++)
        {
            c = e[o.charAt(x)];
            b = (b << 6) + c;
            l += 6;
            while (l >= 8)
            {
                r += w((b >>> (l -= 8)) % 256);
            }
         }
    }
    return r;
}
Snajper
źródło
6
Wygląda na to, że Opera 11.62 ma problem z częścią „% 256”. Zastąpienie go przez „& 0xff” powoduje, że działa.
Yaroslav Stavnichiy
Wydaje się, że kod javascript wirtualnego punktu końcowego ma problem z częścią „\ x00”. Zastąpienie go r = r.replace (/ \ x00 / g, '') sprawia, że ​​działa
Panupong Kongarn
11

Projekt php.js ma implementacje JavaScript wielu funkcji PHP. base64_encodei base64_decodesą uwzględnione.

ceejayoz
źródło
php.js jest wcieleniem wszelkiego zła i należy do własnej warstwy w piekle. Unikaj tego jak zarazy. (Więcej informacji: softwareengineering.stackexchange.com/questions/126671/... )
Coreus
Nie widzę większego wsparcia dla tego ogólnego twierdzenia w tym linku, @Coreus. Używana rozważnie lub jako punkt wyjścia, jest to całkowicie akceptowalny sposób na znalezienie logiki równoważnej w JS dla czegoś, co już wiesz, jak robić w PHP.
ceejayoz
8

Czy ktoś powiedział kod golfa? =)

Oto moja próba poprawienia mojego upośledzenia podczas nadrabiania zaległości. Dostarczone dla Twojej wygody.

function decode_base64(s) {
  var b=l=0, r='',
  m='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  s.split('').forEach(function (v) {
    b=(b<<6)+m.indexOf(v); l+=6;
    if (l>=8) r+=String.fromCharCode((b>>>(l-=8))&0xff);
  });
  return r;
}

To, czego właściwie szukałem, to implementacja asynchroniczna i ku mojemu zaskoczeniu okazuje się, forEachże w przeciwieństwie do $([]).eachimplementacji metody JQuery jest bardzo synchroniczna.

Jeśli miałeś na myśli również takie zwariowane pojęcia, opóźnienie 0 window.setTimeouturuchomi asynchronicznie dekodowanie base64 i wykona funkcję zwrotną z wynikiem po zakończeniu.

function decode_base64_async(s, cb) {
  setTimeout(function () { cb(decode_base64(s)); }, 0);
}

@Toothbrush zasugerował „zindeksować ciąg znaków jak tablicę” i pozbyć się split. Ta rutyna wydaje się naprawdę dziwna i nie jestem pewna, jak będzie kompatybilna, ale uderza innego ptaszka, więc niech to zostanie.

function decode_base64(s) {
  var b=l=0, r='',
  m='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  [].forEach.call(s, function (v) {
    b=(b<<6)+m.indexOf(v); l+=6;
    if (l>=8) r+=String.fromCharCode((b>>>(l-=8))&0xff);
  });
  return r;
}

Próbując znaleźć więcej informacji na temat ciągu JavaScript jako tablicy, natknąłem się na tę profesjonalną wskazówkę za pomocą /./gwyrażenia regularnego, aby przejść przez ciąg. To jeszcze bardziej zmniejsza rozmiar kodu, zastępując ciąg znaków na miejscu i eliminując potrzebę utrzymywania zmiennej zwracanej.

function decode_base64(s) {
  var b=l=0,
  m='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  return s.replace(/./g, function (v) {
    b=(b<<6)+m.indexOf(v); l+=6;
    return l<8?'':String.fromCharCode((b>>>(l-=8))&0xff);
  });
}

Jeśli jednak szukasz czegoś bardziej tradycyjnego, być może podoba Ci się coś więcej.

function decode_base64(s) {
  var b=l=0, r='', s=s.split(''), i,
  m='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  for (i in s) {
    b=(b<<6)+m.indexOf(s[i]); l+=6;
    if (l>=8) r+=String.fromCharCode((b>>>(l-=8))&0xff);
  }
  return r;
}

Nie miałem problemu z końcową wartością zerową, więc został on usunięty, aby pozostać poniżej normy, ale powinien być łatwo rozwiązany za pomocą a trim()lub a, trimRight()jeśli wolisz, jeśli stanowi to dla ciebie problem.

to znaczy.

return r.trimRight();

Uwaga:

Wynikiem jest ciąg bajtów ascii, jeśli potrzebujesz Unicode, najłatwiejszy jest escapeciąg bajtów, który można następnie zdekodować w decodeURIComponentcelu utworzenia łańcucha Unicode.

function decode_base64_usc(s) {      
  return decodeURIComponent(escape(decode_base64(s)));
}

Ponieważ escapejest on przestarzały, możemy zmienić naszą funkcję, aby obsługiwała Unicode bezpośrednio, bez potrzeby escapelub String.fromCharCodemożemy wygenerować %ciąg znaków ucieczki gotowy do dekodowania URI.

function decode_base64(s) {
  var b=l=0,
  m='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
  return decodeURIComponent(s.replace(/./g, function (v) {
    b=(b<<6)+m.indexOf(v); l+=6;
    return l<8?'':'%'+(0x100+((b>>>(l-=8))&0xff)).toString(16).slice(-2);
  }));
}

nJoy!

nikiel
źródło
3
splitCiąg nie jest potrzebny , ponieważ można indeksować ciąg JavaScript jak tablicę. s.split('').forEach(function ...można zastąpić [].forEach.call(s, function .... Powinno być znacznie szybsze, ponieważ nie musi on dzielić struny.
Szczoteczka do zębów
Ten „bardziej tradycyjny” został dla mnie całkowicie zepsuty - wytworzył zniekształcony bałagan ze śladami oryginalnego tekstu. W Chrome.
Steve Bennett
@ Steve Bennett Przetestowałem wszystkie warianty w chromie ... działa. Czy możesz podać przykładowy ciąg base64, który się nie powiedzie?
nickl
Trzy lata później? Ha. Nawet nie pamiętam, do czego tego potrzebowałem.
Steve Bennett
6

Próbowałem procedur JavaScript w phpjs.org i działały dobrze.

Najpierw wypróbowałem procedury sugerowane w wybranej odpowiedzi Ranhiru Cooray - http://ntt.cc/2008/01/19/base64-encoder-decoder-with-javascript.html

Odkryłem, że nie działały one we wszystkich okolicznościach. Napisałem przypadek testowy, w którym te procedury się nie powiodły, i opublikowałem je w GitHub pod adresem:

https://github.com/scottcarter/base64_javascript_test_data.git

Wysłałem również komentarz do posta na blogu w witrynie ntt.cc, aby ostrzec autora (w oczekiwaniu na moderację - artykuł jest stary, więc nie jestem pewien, czy komentarz zostanie opublikowany).

Scott Carter
źródło
1

Wolę używać metod kodowania / dekodowania bas64 z CryptoJS , najpopularniejszej biblioteki standardowych i bezpiecznych algorytmów kryptograficznych zaimplementowanych w JavaScript z wykorzystaniem najlepszych praktyk i wzorców.

Dan Dascalescu
źródło
1

W Node.js możemy to zrobić w prosty sposób

var base64 = 'SGVsbG8gV29ybGQ='
var base64_decode = new Buffer(base64, 'base64').toString('ascii');

console.log(base64_decode); // "Hello World"
KARTHIKEYAN.A
źródło
0

W przypadku frameworków JavaScript, w których nie ma atobmetody i jeśli nie chcesz importować bibliotek zewnętrznych, jest to krótka funkcja, która to robi.

Otrzymałby ciąg zawierający wartość zakodowaną w Base64 i zwrócił zdekodowaną tablicę bajtów (gdzie tablica bajtów jest reprezentowana jako tablica liczb, gdzie każda liczba jest liczbą całkowitą od 0 do 255 włącznie).

function fromBase64String(str) {
    var alpha = 
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    var value = [];
    var index = 0;
    var destIndex  = 0;
    var padding = false;
    while (true) {

        var first  = getNextChr(str, index, padding, alpha);
        var second = getNextChr(str, first .nextIndex, first .padding, alpha);
        var third  = getNextChr(str, second.nextIndex, second.padding, alpha);
        var fourth = getNextChr(str, third .nextIndex, third .padding, alpha);

        index = fourth.nextIndex;
        padding = fourth.padding;

        // ffffffss sssstttt ttffffff
        var base64_first  = first.code  == null ? 0 : first.code;
        var base64_second = second.code == null ? 0 : second.code;
        var base64_third  = third.code  == null ? 0 : third.code;
        var base64_fourth = fourth.code == null ? 0 : fourth.code;

        var a = (( base64_first << 2) & 0xFC ) | ((base64_second>>4) & 0x03);
        var b = (( base64_second<< 4) & 0xF0 ) | ((base64_third >>2) & 0x0F);
        var c = (( base64_third << 6) & 0xC0 ) | ((base64_fourth>>0) & 0x3F);

        value [destIndex++] = a;
        if (!third.padding) {
            value [destIndex++] = b;
        } else {
            break;
        }
        if (!fourth.padding) {
            value [destIndex++] = c;
        } else {
            break;
        }
        if (index >= str.length) {
            break;
        }
    }
    return value;
}

function getNextChr(str, index, equalSignReceived, alpha) {
    var chr = null;
    var code = 0;
    var padding = equalSignReceived;
    while (index < str.length) {
        chr = str.charAt(index);
        if (chr == " " || chr == "\r" || chr == "\n" || chr == "\t") {
            index++;
            continue;
        }
        if (chr == "=") {
            padding = true;
        } else {
            if (equalSignReceived) {
                throw new Error("Invalid Base64 Endcoding character \"" 
                    + chr + "\" with code " + str.charCodeAt(index) 
                    + " on position " + index 
                    + " received afer an equal sign (=) padding "
                    + "character has already been received. "
                    + "The equal sign padding character is the only "
                    + "possible padding character at the end.");
            }
            code = alpha.indexOf(chr);
            if (code == -1) {
                throw new Error("Invalid Base64 Encoding character \"" 
                    + chr + "\" with code " + str.charCodeAt(index) 
                    + " on position " + index + ".");
            }
        }
        break;
    }
    return { character: chr, code: code, padding: padding, nextIndex: ++index};
}

Wykorzystane zasoby: RFC-4648 Sekcja 4

Yordan Nedelchev
źródło