Encodage et décodage Base64 en Javascript côté client

Réponses:

214

Certains navigateurs tels que Firefox, Chrome, Safari, Opera et IE10 + peuvent gérer Base64 de manière native. Jetez un oeil à cette question Stackoverflow . Il utilise btoa()et atob()fonctionne .

Pour JavaScript côté serveur (Node), vous pouvez utiliser Buffers pour décoder.

Si vous optez pour une solution multi-navigateur, il existe des bibliothèques existantes comme CryptoJS ou du code comme:

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

Avec ce dernier, vous devez tester minutieusement la fonction pour la compatibilité entre les navigateurs. Et une erreur a déjà été signalée .

Ranhiru Jude Cooray
la source
1
J'ai utilisé cette méthode pour coder un SVG en base64 avec le schéma URI Date. Surprise: cette fonction code pour chaque caractère, donc j'obtiens ce XML mal formé sur la cible:% 3C% 3Fxml% 20version% 3D% 271.0% 27% 20% 3F% 3E% 3Csvg% 20xmlns% 3D% 27http% ...
Dereckson
21
Node.js peut faire Base64 nativement: new Buffer('Hello, world!').toString('base64'); new Buffer('SGVsbG8sIHdvcmxkIQ==', 'base64').toString('ascii'); (source)
nyuszika7h
Et si je voulais le faire sans URL?
anddero
1
dans le nœud 5+, utilisez Buffer.from, comme il new Buffer(string)est déconseillé. Buffer.from(jwt.split('.')[1], 'base64').toString()
Ray Foss
63

Dans les navigateurs basés sur Gecko / WebKit (Firefox, Chrome et Safari) et Opera, vous pouvez utiliser btoa () et atob () .

Réponse originale: Comment pouvez-vous encoder une chaîne en Base64 en JavaScript?

nyuszika7h
la source
C'est un épargnant de vie. J'ai utilisé quelques implémentations différentes pour décoder de très grandes chaînes encodées en base64 et le résultat était toujours faux. atob () fonctionne très bien!
b2238488
15
Petite astuce: Opera n'est pas basé sur Gecko ou Webkit, il utilise son propre moteur de rendu appelé Presto.
Peter Olson
Wow, merci pour ça. Je ne savais pas qu'il y avait un encodeur natif base64 dans ces navigateurs!
Rob Porter,
5
@PeterOlson Pas plus :)
Mustafa
1
Je me rends compte que c'est un vieux post, mais à propos de la préoccupation de @ b2238488, vous pouvez diviser la chaîne base64 de sorte que la longueur de chaque jeton soit un multiple de 4, et les décoder séparément. Le résultat sera le même que le décodage de la chaîne entière à la fois.
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!"

Navigateur croisé

Bibliothèques / modules de codage et de décodage Javascript UTF-8 et Base64 réécrits et modulaires pour AMD, CommonJS, Nodejs et navigateurs. Compatible avec plusieurs navigateurs.


avec Node.js

Voici comment vous encodez du texte normal en base64 dans 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');

Et voici comment décoder les chaînes encodées en base64:

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

avec Dojo.js

Pour coder un tableau d'octets à l'aide de dojox.encoding.base64:

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

Pour décoder une chaîne codée en base64:

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

bower install angular-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=');
}]);

Mais comment?

Si vous souhaitez en savoir plus sur la façon dont le codage base64 est en général, et en JavaScript en particulier, je recommanderais cet article: Informatique en JavaScript: codage Base64

davidcondrey
la source
1
FYI: La version multi-navigateur a quelques fuites désagréables avec c2et probablement c1et c3donc elle ne fonctionnera pas avec "use strict"comme défini ci-dessus.
Campbeln
41

Voici une version resserrée du post de Sniper. Il suppose une chaîne base64 bien formée sans retour chariot. Cette version élimine quelques boucles, ajoute le &0xffcorrectif de Yaroslav, élimine les null à la fin, plus un peu de golf de code.

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
la source
5
Encore moins d'octets; 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
Oui, mais ne fonctionne qu'avec ASCII. Les caractères Cyr sont mutilés par exemple.
Martin Kovachev
@MartinKovachev pouvez-vous poster un nouveau commentaire avec un exemple de texte avec des caractères Cyr et l'encodage base64 correspondant? Peut-être que nous pouvons corriger le code pour l'adapter.
broc.seib
Ici: quelque chose comme ça: тестова фраза
Martin Kovachev
2
@OliverSalzburg Encore moins de génération de table de code :):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

Fonction de décodage JavaScript Base64 courte et rapide sans sécurité intégrée:

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;
}
Tireur d'élite
la source
6
Opera 11.62 semble avoir un problème avec la partie '% 256'. Le remplacer par '& 0xff' le fait fonctionner.
Yaroslav Stavnichiy
Le code javascript du point de terminaison virtuel Tyk semble avoir un problème avec la partie «\ x00». Le remplacer par r = r.replace (/ \ x00 / g, '') pour le faire fonctionner
Panupong Kongarn
11

Le projet php.js a des implémentations JavaScript de nombreuses fonctions PHP. base64_encodeet base64_decodesont inclus.

ceejayoz
la source
php.js est l'incarnation de tout mal et appartient à sa propre couche en enfer. A éviter comme la peste. (Plus d'informations: softwareengineering.stackexchange.com/questions/126671/… )
Coreus
Je ne vois pas grand-chose soutenir cette affirmation générale dans ce lien, @Coreus. Utilisé judicieusement, ou comme point de départ, c'est un moyen parfaitement acceptable de comprendre la logique équivalente dans JS pour quelque chose que vous savez déjà comment faire en PHP.
ceejayoz le
9

Quelqu'un a-t-il dit le code golf? =)

Ce qui suit est ma tentative d'améliorer mon handicap tout en rattrapant le temps. Fourni pour votre commodité.

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

Ce que je cherchais en fait était une implémentation asynchrone et à ma grande surprise, il s'avère forEachque contrairement à l' $([]).eachimplémentation de la méthode JQuery, elle est très synchrone.

Si vous aviez également des notions aussi folles à l'esprit, un retard de 0 window.setTimeoutexécutera le décodage base64 de manière asynchrone et exécutera la fonction de rappel avec le résultat une fois terminé.

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

@Toothbrush a suggéré "d'indexer une chaîne comme un tableau" et de se débarrasser de split. Cette routine semble vraiment étrange et je ne sais pas à quel point elle sera compatible, mais elle frappe un autre birdie, alors allons-y.

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

En essayant de trouver plus d'informations sur la chaîne JavaScript en tant que tableau, je suis tombé sur cette astuce de pro en utilisant une /./gexpression régulière pour parcourir une chaîne. Cela réduit encore plus la taille du code en remplaçant la chaîne en place et en éliminant le besoin de conserver une variable de retour.

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

Si toutefois vous cherchiez quelque chose d'un peu plus traditionnel, peut-être que ce qui suit est plus à votre goût.

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

Je n'avais pas le problème null de fin, donc cela a été supprimé pour rester sous le pair mais il devrait facilement être résolu avec un trim()ou un trimRight()si vous préférez, si cela vous pose un problème.

c'est à dire.

return r.trimRight();

Remarque:

Le résultat est une chaîne d'octets ascii, si vous avez besoin d'unicode, le plus simple est escapela chaîne d'octets qui peut ensuite être décodée avec decodeURIComponentpour produire la chaîne unicode.

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

Comme il escapeest obsolète, nous pourrions changer notre fonction pour prendre en charge directement unicode sans avoir besoin de escapeou String.fromCharCodenous pouvons produire une %chaîne d'échappement prête pour le décodage 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!

nickl-
la source
3
Vous n'avez pas besoin de splitla chaîne, car vous pouvez indexer une chaîne JavaScript comme un tableau. s.split('').forEach(function ...peut être remplacé par [].forEach.call(s, function .... Cela devrait être beaucoup plus rapide, car il n'a pas à diviser la chaîne.
Brosse à dents
Le "plus traditionnel" a été complètement cassé pour moi - a produit un désordre brouillé avec des traces de texte original saupoudré partout. Sur Chrome.
Steve Bennett
@Steve Bennett J'ai testé toutes les variantes en chrome ... ça marche. Pouvez-vous fournir un exemple de chaîne base64 qui échoue?
nickl-
Trois ans plus tard? Ha. Je ne me souviens même pas pour quoi j'avais besoin de ça.
Steve Bennett
6

J'ai essayé les routines Javascript sur phpjs.org et elles ont bien fonctionné.

J'ai d'abord essayé les routines suggérées dans la réponse choisie par Ranhiru Cooray - http://ntt.cc/2008/01/19/base64-encoder-decoder-with-javascript.html

J'ai trouvé qu'ils ne fonctionnaient pas en toutes circonstances. J'ai rédigé un cas de test où ces routines échouent et les ai publiées sur GitHub à l'adresse:

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

J'ai également posté un commentaire sur le blog sur ntt.cc pour alerter l'auteur (en attente de modération - l'article est ancien, donc je ne sais pas si le commentaire sera publié).

Scott Carter
la source
1

Je préfère utiliser les méthodes d'encodage / décodage bas64 de CryptoJS , la bibliothèque la plus populaire pour les algorithmes cryptographiques standard et sécurisés implémentés en JavaScript en utilisant les meilleures pratiques et modèles.

Dan Dascalescu
la source
1

Dans Node.js, nous pouvons le faire de manière simple

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

console.log(base64_decode); // "Hello World"
KARTHIKEYAN.A
la source
0

Pour les frameworks JavaScripts où il n'y a pas de atobméthode et au cas où vous ne souhaitez pas importer de bibliothèques externes, c'est une fonction courte qui le fait.

Il obtiendrait une chaîne qui contient une valeur encodée en Base64 et retournera un tableau décodé d'octets (où le tableau d'octets est représenté comme un tableau de nombres où chaque nombre est un entier compris entre 0 et 255 inclus).

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

Ressources utilisées: RFC-4648 Section 4

Yordan Nedelchev
la source