Codificación y decodificación Base64 en Javascript del lado del cliente

Respuestas:

214

Algunos navegadores como Firefox, Chrome, Safari, Opera e IE10 + pueden manejar Base64 de forma nativa. Echa un vistazo a esta pregunta de Stackoverflow . Está usando btoa()y atob()funciones .

Para JavaScript del lado del servidor (Nodo), puede usar Buffers para decodificar.

Si está buscando una solución de navegador cruzado, hay bibliotecas existentes como CryptoJS o código como:

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

Con este último, debe probar a fondo la función para la compatibilidad entre navegadores. Y el error ya ha sido reportado .

Ranhiru Jude Cooray
fuente
1
Usé este método para codificar un SVG en base64 con el esquema Date URI. Sorpresa: esta función codifica cada carácter, por lo que obtengo este XML con formato incorrecto en el objetivo:% 3C% 3Fxml% 20version% 3D% 271.0% 27% 20% 3F% 3E% 3Csvg% 20xmlns% 3D% 27http% ...
Dereckson
21
Node.js puede hacer Base64 de forma nativa: new Buffer('Hello, world!').toString('base64'); new Buffer('SGVsbG8sIHdvcmxkIQ==', 'base64').toString('ascii'); (fuente)
nyuszika7h
¿Y si quisiera hacerlo con seguridad de URL?
anddero
1
en el nodo 5+, use Buffer.from, como new Buffer(string)está en desuso. Buffer.from(jwt.split('.')[1], 'base64').toString()
Ray Foss
63

En los navegadores basados ​​en Gecko / WebKit (Firefox, Chrome y Safari) y Opera, puede usar btoa () y atob () .

Respuesta original: ¿Cómo puede codificar una cadena a Base64 en JavaScript?

nyuszika7h
fuente
Este es un salvavidas. Utilicé algunas implementaciones diferentes para decodificar cadenas codificadas en base64 muy grandes y el resultado siempre fue incorrecto. ¡atob () funciona muy bien!
b2238488
15
Pequeño detalle: Opera no se basa en Gecko o Webkit, usa su propio motor de renderizado llamado Presto.
Peter Olson
Wow, gracias por esto. ¡No sabía que había un codificador nativo de base64 en estos navegadores!
Rob Porter
55
@PeterOlson ya no :)
Mustafa
1
Me doy cuenta de que esta es una publicación antigua, pero sobre la preocupación de @ b2238488, puede dividir la cadena base64 para que la longitud de cada token sea un múltiplo de 4 y decodificarlos por separado. El resultado será el mismo que decodificar toda la cadena a la vez.
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!"

Navegador cruzado

Bibliotecas / módulos de codificación y decodificación Javascript UTF-8 y Base64 reescritas y modularizadas para AMD, CommonJS, Nodejs y navegadores. Compatible con navegador cruzado.


con Node.js

Así es como codifica el texto normal a base64 en 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');

Y así es como decodifica cadenas codificadas en base64:

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

con Dojo.js

Para codificar una matriz de bytes usando dojox.encoding.base64:

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

Para decodificar una cadena codificada en base64:

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

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

¿Pero cómo?

Si desea obtener más información sobre cómo se codifica base64 en general, y en JavaScript en particular, le recomendaría este artículo: Informática en JavaScript: codificación Base64

davidcondrey
fuente
1
FYI: La versión multi-navegador tiene algunas fugas desagradables con c2y es probable c1y c3lo que no funciona con "use strict"los definidos anteriormente.
Campbeln
41

Aquí hay una versión más estricta de la publicación de Sniper. Presume una cadena base64 bien formada sin retornos de carro. Esta versión elimina un par de bucles, agrega la &0xffsolución de Yaroslav, elimina los nulos finales, además de un poco de código de golf.

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
fuente
55
Incluso menos bytes; 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
Sí, pero solo funciona con ASCII. Los personajes de Cyr se destrozan, por ejemplo.
Martin Kovachev
@MartinKovachev ¿puede publicar un nuevo comentario con texto de ejemplo con caracteres Cyr y la codificación base64 correspondiente? Tal vez podamos arreglar el código para acomodarlo.
broc.seib
Aquí: algo así: тестова фраза
Martin Kovachev
2
@OliverSalzburg Aún menos generar tabla de códigos :):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

Función de decodificación de JavaScript Base64 corta y rápida sin seguridad:

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;
}
Francotirador
fuente
66
Opera 11.62 parece tener problemas con la parte '% 256'. Reemplazarlo con '& 0xff' lo hace funcionar.
Yaroslav Stavnichiy
El código JavaScript de punto final virtual de Tyk parece tener problemas con la parte '\ x00'. Reemplazándolo con r = r.replace (/ \ x00 / g ''), haga que funcione
Panupong Kongarn
11

El proyecto php.js tiene implementaciones de JavaScript de muchas de las funciones de PHP. base64_encodey base64_decodeestán incluidos

ceejayoz
fuente
php.js es la encarnación de todo mal y pertenece a su propia capa en el infierno. Evítalo como la peste. (Más información: softwareengineering.stackexchange.com/questions/126671/… )
Coreus
No veo mucho que respalde esa afirmación general en ese enlace, @Coreus. Utilizado juiciosamente, o como punto de partida, es una forma perfectamente aceptable de descubrir la lógica equivalente en JS para algo que ya sabes cómo hacer en PHP.
ceejayoz
9

¿Alguien dijo código golf? =)

El siguiente es mi intento de mejorar mi discapacidad mientras me pongo al día. Suministrado para su conveniencia.

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

Lo que realmente buscaba era una implementación asincrónica y, para mi sorpresa, resulta forEachque la $([]).eachimplementación del método de JQuery es muy sincrónica.

Si también tenía en mente esas ideas locas, un retraso de 0 window.setTimeoutejecutará la decodificación base64 de forma asincrónica y ejecutará la función de devolución de llamada con el resultado cuando haya terminado.

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

@Toothbrush sugirió "indexar una cadena como una matriz" y deshacerse de split. Esta rutina parece realmente extraña y no estoy seguro de cuán compatible será, pero golpea a otro pajarito, así que vamos a tenerla.

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

Mientras intentaba encontrar más información sobre la cadena de JavaScript como matriz, me topé con este consejo profesional usando una /./gexpresión regular para recorrer una cadena. Esto reduce aún más el tamaño del código al reemplazar la cadena en su lugar y eliminar la necesidad de mantener una variable de retorno.

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

Sin embargo, si estaba buscando algo un poco más tradicional, quizás lo siguiente sea más de su agrado.

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

No tenía el problema nulo final, por lo que se eliminó para permanecer bajo par, pero debería resolverse fácilmente con un trim()o untrimRight() si lo prefiere, si esto le plantea un problema.

es decir.

return r.trimRight();

Nota:

El resultado es una cadena de bytes ascii, si necesita unicode, lo más fácil es escapela cadena de bytes que luego se puede decodificar decodeURIComponentpara producir la cadena unicode.

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

Dado que escapeestá en desuso, podríamos cambiar nuestra función para admitir unicode directamente sin la necesidad escapeo String.fromCharCodepodemos producir una %cadena de escape lista para la decodificación de 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-
fuente
3
No necesita splitla cadena, ya que puede indexar una cadena de JavaScript como una matriz. s.split('').forEach(function ...puede ser sustituido por [].forEach.call(s, function .... Debería ser mucho más rápido, ya que no tiene que dividir la cadena.
Cepillo de dientes
El "más tradicional" estaba completamente roto para mí: produjo un desastre confuso con rastros de texto original esparcidos por todas partes. En Chrome
Steve Bennett
@ Steve Bennett Probé todas las variantes en Chrome ... funciona. ¿Puede proporcionar una cadena de base64 de ejemplo que falle?
nickl-
¿Tres años despues? Decir ah. Ni siquiera recuerdo para qué necesitaba esto.
Steve Bennett
6

He probado las rutinas de Javascript en phpjs.org y han funcionado bien.

Primero probé las rutinas sugeridas en la respuesta elegida por Ranhiru Cooray - http://ntt.cc/2008/01/19/base64-encoder-decoder-with-javascript.html

Descubrí que no funcionaban en todas las circunstancias. Escribí un caso de prueba donde estas rutinas fallan y las publiqué en GitHub en:

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

También publiqué un comentario en la publicación del blog en ntt.cc para alertar al autor (en espera de moderación; el artículo es antiguo, así que no estoy seguro de si se publicará un comentario).

Scott Carter
fuente
1

Prefiero usar los métodos de codificación / decodificación bas64 de CryptoJS , la biblioteca más popular para algoritmos criptográficos estándar y seguros implementados en JavaScript utilizando las mejores prácticas y patrones.

Dan Dascalescu
fuente
1

En Node.js podemos hacerlo de manera simple

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

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

Para frameworks JavaScripts donde no hay atob método y en caso de que no desee importar bibliotecas externas, esta es una función corta que lo hace.

Obtendría una cadena que contiene un valor codificado en Base64 y devolverá una matriz decodificada de bytes (donde la matriz de bytes se representa como una matriz de números donde cada número es un número entero entre 0 y 255 inclusive).

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

Recursos utilizados: RFC-4648 Sección 4

Yordan Nedelchev
fuente