¿Cómo se puede codificar una cadena a Base64 en JavaScript?

810

Tengo un script PHP que puede codificar una imagen PNG en una cadena Base64.

Me gustaría hacer lo mismo usando JavaScript. Sé cómo abrir archivos, pero no estoy seguro de cómo hacer la codificación. No estoy acostumbrado a trabajar con datos binarios.

nombre de usuario
fuente
2
Aquí está la mejor manera de base64_encode y base64_decode usando javascript. Ver los enlaces a continuación. phpjs.org/functions/base64_encode:358 phpjs.org/functions/base64_decode:357
gautamlakum
aquí hay otro complemento de jquery para codificar / decodificar base64
zahid9i
Compruebe microjs: microjs.com/#base64
Vinod Srivastav el

Respuestas:

866

Puede usar btoa()y atob()para convertir ay desde la codificación base64.

Parece haber cierta confusión en los comentarios sobre lo que estas funciones aceptan / devuelven, así que ...

  • btoa()acepta una "cadena" donde cada carácter representa un byte de 8 bits; si pasa una cadena que contiene caracteres que no se pueden representar en 8 bits, probablemente se romperá . Esto no es un problema si realmente está tratando la cadena como una matriz de bytes, pero si está tratando de hacer algo más, primero tendrá que codificarla.

  • atob()devuelve una "cadena" donde cada carácter representa un byte de 8 bits, es decir, su valor estará entre 0y 0xff. Esto no significa que sea ASCII: presumiblemente si está utilizando esta función, espera trabajar con datos binarios y no con texto.

Ver también:

Shog9
fuente
47
Tenga en cuenta que esto también funciona para los navegadores webkit, como Safari.
Daniel Von Fange
55
pero no funciona en iPhone3G con iOS 4.1. Funciona en el simulador de simulador de iPhone cuando se configura en iPhone4 o iPhone.
Grant M
29
Tenga en cuenta la consideración especial para las cadenas Unicode: developer.mozilla.org/En/DOM/Window.btoa#Unicode_Strings btoa y atob solo funcionan correctamente para cadenas basadas en ASCII. Como estadounidense, probablemente no notarás una diferencia ... pero la primera vez que uses un carácter acentuado, tu código se romperá.
Dan Esparza
70
debe usar btoa(unescape(encodeURIComponent(str))))si str es UFT8
SET
44
Vea mi edición, @Triynko. Estos no están destinados a ser utilizados para procesar texto , punto.
Shog9
289

Desde aquí :

/**
*
*  Base64 encode / decode
*  http://www.webtoolkit.info/
*
**/
var Base64 = {

// private property
_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

// public method for encoding
encode : function (input) {
    var output = "";
    var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
    var i = 0;

    input = Base64._utf8_encode(input);

    while (i < input.length) {

        chr1 = input.charCodeAt(i++);
        chr2 = input.charCodeAt(i++);
        chr3 = input.charCodeAt(i++);

        enc1 = chr1 >> 2;
        enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
        enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
        enc4 = chr3 & 63;

        if (isNaN(chr2)) {
            enc3 = enc4 = 64;
        } else if (isNaN(chr3)) {
            enc4 = 64;
        }

        output = output +
        this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
        this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);

    }

    return output;
},

// public method for decoding
decode : function (input) {
    var output = "";
    var chr1, chr2, chr3;
    var enc1, enc2, enc3, enc4;
    var i = 0;

    input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

    while (i < input.length) {

        enc1 = this._keyStr.indexOf(input.charAt(i++));
        enc2 = this._keyStr.indexOf(input.charAt(i++));
        enc3 = this._keyStr.indexOf(input.charAt(i++));
        enc4 = this._keyStr.indexOf(input.charAt(i++));

        chr1 = (enc1 << 2) | (enc2 >> 4);
        chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
        chr3 = ((enc3 & 3) << 6) | enc4;

        output = output + String.fromCharCode(chr1);

        if (enc3 != 64) {
            output = output + String.fromCharCode(chr2);
        }
        if (enc4 != 64) {
            output = output + String.fromCharCode(chr3);
        }

    }

    output = Base64._utf8_decode(output);

    return output;

},

// private method for UTF-8 encoding
_utf8_encode : function (string) {
    string = string.replace(/\r\n/g,"\n");
    var utftext = "";

    for (var n = 0; n < string.length; n++) {

        var c = string.charCodeAt(n);

        if (c < 128) {
            utftext += String.fromCharCode(c);
        }
        else if((c > 127) && (c < 2048)) {
            utftext += String.fromCharCode((c >> 6) | 192);
            utftext += String.fromCharCode((c & 63) | 128);
        }
        else {
            utftext += String.fromCharCode((c >> 12) | 224);
            utftext += String.fromCharCode(((c >> 6) & 63) | 128);
            utftext += String.fromCharCode((c & 63) | 128);
        }

    }

    return utftext;
},

// private method for UTF-8 decoding
_utf8_decode : function (utftext) {
    var string = "";
    var i = 0;
    var c = c1 = c2 = 0;

    while ( i < utftext.length ) {

        c = utftext.charCodeAt(i);

        if (c < 128) {
            string += String.fromCharCode(c);
            i++;
        }
        else if((c > 191) && (c < 224)) {
            c2 = utftext.charCodeAt(i+1);
            string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
            i += 2;
        }
        else {
            c2 = utftext.charCodeAt(i+1);
            c3 = utftext.charCodeAt(i+2);
            string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
            i += 3;
        }

    }

    return string;
}

}

Además, la búsqueda en "codificación javascript base64" activa muchas otras opciones, la anterior fue la primera.

Sunny Milenov
fuente
3
Esto también es útil cuando la codificación base64 no es estándar; en mi caso no se utilizó el carácter "/" y el "?" en su lugar se usó el carácter, lo que significa que incluso en Chrome atob () no iba a decodificar las cadenas base64 que estaban entrando.
Chris Moschini
21
Tenga cuidado con este código: intenta interpretar su cadena como una cadena codificada UTF-8. Tuvimos un caso en el que teníamos una cadena binaria (es decir, cada carácter de la cadena debería interpretarse como un byte), y este código corrompió los datos. Lee la fuente, Luke.
Daniel Yankowsky
11
Todo lo que se necesita para que sea seguro para la mayoría de las codificaciones / decodificaciones binarias es eliminar la string = string.replace(/\r\n/g,"\n");declaración cuestionable en el método de codificación utf8.
Marius
77
@ Mario: Me pregunto por qué lo incluirían string = string.replace(/\r\n/g,"\n");en primer lugar, jajaja. Es como "oh, codifiquemos esta cadena, pero primero, ¿por qué no normalizamos aleatoriamente todos los saltos de línea sin ninguna razón?". Eso debería eliminarse absolutamente de la clase en todas las circunstancias.
Triynko
2
No soy un gurú de JavaScript, pero este código parece contener un error: si chr2 es NaN, su valor todavía se usa en la declaración enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);. En mi navegador, esto funciona bien, NaN>>4es igual a 0, pero no sé si todos los navegadores hacen esto (también, NaN/16es igual a NaN).
Jan
117

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

// Create Base64 Object
var Base64={_keyStr:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",encode:function(e){var t="";var n,r,i,s,o,u,a;var f=0;e=Base64._utf8_encode(e);while(f<e.length){n=e.charCodeAt(f++);r=e.charCodeAt(f++);i=e.charCodeAt(f++);s=n>>2;o=(n&3)<<4|r>>4;u=(r&15)<<2|i>>6;a=i&63;if(isNaN(r)){u=a=64}else if(isNaN(i)){a=64}t=t+this._keyStr.charAt(s)+this._keyStr.charAt(o)+this._keyStr.charAt(u)+this._keyStr.charAt(a)}return t},decode:function(e){var t="";var n,r,i;var s,o,u,a;var f=0;e=e.replace(/[^A-Za-z0-9\+\/\=]/g,"");while(f<e.length){s=this._keyStr.indexOf(e.charAt(f++));o=this._keyStr.indexOf(e.charAt(f++));u=this._keyStr.indexOf(e.charAt(f++));a=this._keyStr.indexOf(e.charAt(f++));n=s<<2|o>>4;r=(o&15)<<4|u>>2;i=(u&3)<<6|a;t=t+String.fromCharCode(n);if(u!=64){t=t+String.fromCharCode(r)}if(a!=64){t=t+String.fromCharCode(i)}}t=Base64._utf8_decode(t);return t},_utf8_encode:function(e){e=e.replace(/\r\n/g,"\n");var t="";for(var n=0;n<e.length;n++){var r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r)}else if(r>127&&r<2048){t+=String.fromCharCode(r>>6|192);t+=String.fromCharCode(r&63|128)}else{t+=String.fromCharCode(r>>12|224);t+=String.fromCharCode(r>>6&63|128);t+=String.fromCharCode(r&63|128)}}return t},_utf8_decode:function(e){var t="";var n=0;var r=c1=c2=0;while(n<e.length){r=e.charCodeAt(n);if(r<128){t+=String.fromCharCode(r);n++}else if(r>191&&r<224){c2=e.charCodeAt(n+1);t+=String.fromCharCode((r&31)<<6|c2&63);n+=2}else{c2=e.charCodeAt(n+1);c3=e.charCodeAt(n+2);t+=String.fromCharCode((r&15)<<12|(c2&63)<<6|c3&63);n+=3}}return t}}

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

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

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

jsFiddle


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=');
}]);
davidcondrey
fuente
3
Esta respuesta se basa en el código original y NO incluye actualizaciones a ese código publicado en otras respuestas aquí.
Eugene Ryabtsev
La solución NodeJS propuesta está en desuso.
Vladimir Nul
94

El código de Sunny es excelente, excepto que se rompe en IE7 debido a referencias a "esto". Solucionado reemplazando tales referencias con "Base64":

var Base64 = {
// private property
_keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

// public method for encoding
encode : function (input) {
    var output = "";
    var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
    var i = 0;

    input = Base64._utf8_encode(input);

    while (i < input.length) {

        chr1 = input.charCodeAt(i++);
        chr2 = input.charCodeAt(i++);
        chr3 = input.charCodeAt(i++);

        enc1 = chr1 >> 2;
        enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
        enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
        enc4 = chr3 & 63;

        if (isNaN(chr2)) {
            enc3 = enc4 = 64;
        } else if (isNaN(chr3)) {
            enc4 = 64;
        }

        output = output +
        Base64._keyStr.charAt(enc1) + Base64._keyStr.charAt(enc2) +
        Base64._keyStr.charAt(enc3) + Base64._keyStr.charAt(enc4);

    }

    return output;
},

// public method for decoding
decode : function (input) {
    var output = "";
    var chr1, chr2, chr3;
    var enc1, enc2, enc3, enc4;
    var i = 0;

    input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

    while (i < input.length) {

        enc1 = Base64._keyStr.indexOf(input.charAt(i++));
        enc2 = Base64._keyStr.indexOf(input.charAt(i++));
        enc3 = Base64._keyStr.indexOf(input.charAt(i++));
        enc4 = Base64._keyStr.indexOf(input.charAt(i++));

        chr1 = (enc1 << 2) | (enc2 >> 4);
        chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
        chr3 = ((enc3 & 3) << 6) | enc4;

        output = output + String.fromCharCode(chr1);

        if (enc3 != 64) {
            output = output + String.fromCharCode(chr2);
        }
        if (enc4 != 64) {
            output = output + String.fromCharCode(chr3);
        }

    }

    output = Base64._utf8_decode(output);

    return output;

},

// private method for UTF-8 encoding
_utf8_encode : function (string) {
    string = string.replace(/\r\n/g,"\n");
    var utftext = "";

    for (var n = 0; n < string.length; n++) {

        var c = string.charCodeAt(n);

        if (c < 128) {
            utftext += String.fromCharCode(c);
        }
        else if((c > 127) && (c < 2048)) {
            utftext += String.fromCharCode((c >> 6) | 192);
            utftext += String.fromCharCode((c & 63) | 128);
        }
        else {
            utftext += String.fromCharCode((c >> 12) | 224);
            utftext += String.fromCharCode(((c >> 6) & 63) | 128);
            utftext += String.fromCharCode((c & 63) | 128);
        }

    }

    return utftext;
},

// private method for UTF-8 decoding
_utf8_decode : function (utftext) {
    var string = "";
    var i = 0;
    var c = c1 = c2 = 0;

    while ( i < utftext.length ) {

        c = utftext.charCodeAt(i);

        if (c < 128) {
            string += String.fromCharCode(c);
            i++;
        }
        else if((c > 191) && (c < 224)) {
            c2 = utftext.charCodeAt(i+1);
            string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
            i += 2;
        }
        else {
            c2 = utftext.charCodeAt(i+1);
            c3 = utftext.charCodeAt(i+2);
            string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
            i += 3;
        }

    }
    return string;
}
}
user850789
fuente
44
ooh mi mal, estaba tomando la entrada de la URL del navegador; donde | se convierte a% 7C; de ahí que la codificación también sea incorrecta.
Kanagavelu Sugumar
Sé que esto es realmente antiguo, pero he visto esta función utilizada en más de un lugar, la cadena de teclas en realidad tiene 65 caracteres, no 64. La cadena no es una especificación estándar, no estoy seguro de que importe, pero me preguntaba ¿si lo hace?
Jonathan Wagner
"uso estricto"; es lo que rompe el 'este' y otros elementos de tipo como 'con' y, por lo que he leído, 'eval' recibe un golpe. Todas las ideas fuera de lugar sobre el abuso. Personalmente, no veo por qué JavaScript necesita seguir la ruta, nunca fue un programa estrictamente vinculado y más complejo de lo que ya es. Si quieres estar vinculado, crea un compilador para javascript.
Mark Giblin
Intento usar esta función y recibo el error: Causado por: org.mozilla.javascript.EcmaError: TypeError: No puedo encontrar la función reemplazar en el teste teste teste teste Estoy tratando de codificar .txt con "teste teste teste". Alguien sabe por qué este error?
PRVS
@ JonathanWagner: se usan 64 caracteres para la codificación normal. El 65.o carácter se utiliza como relleno; la cadena de entrada no tiene una cantidad de caracteres divisibles por 3.
Inicio
90

Puede usar btoa(a base-64) y atob(desde base-64).

Para IE 9 y versiones inferiores, pruebe el complemento jquery-base64 :

$.base64.encode("this is a test");
$.base64.decode("dGhpcyBpcyBhIHRlc3Q=");
Vitalii Fedorenko
fuente
133
¿Por qué todo debe ser un complemento de jQuery ?: esto es solo la funcionalidad principal de JavaScript, esto no tiene nada que ver con DOM o jQuery
EaterOfCode
38
Esta no es una funcionalidad principal o no habría tantas respuestas diferentes con votos altos (incluido hágalo usted mismo tl; código dr). Entonces, en mi opinión, este es un buen caso de uso para jQuery (un revestimiento, que se espera que funcione incluso en WebView de Android), incluso más si ya es una dependencia.
Risadinha
1
Me gusta instalar fragmentos de código como este en jQuery principalmente porque existirán en un espacio de nombres controlado. Si no está utilizando AMD o CommonJS o un patrón de diseño similar, es fácil para su espacio de nombres global desordenarse realmente con un montón de funciones aleatorias.
sffc
99
@Risadinha: excepto que su funcionalidad no depende ni extiende nada de jQuery ... literalmente, las únicas referencias a jQuery en su código son adjuntarlo al objeto jQuery ... entonces, ¿cuál es el punto de adjuntarlo a jQuery y, por lo tanto, requiere jQuery para usar? Simplemente haga que sea su propio 1 liner base64.encode(...)y base64.decode(...)... adjuntarlo a jQuery cuando tenga cero funcionalidad específica de jQuery no tiene absolutamente ningún sentido ...
Jimbo Jonny
1
No se solicitó jQuery. No es una respuesta válida a una simple pregunta de JS.
metaColin
34

De los comentarios (por SET y Stefan Steiger) debajo de la respuesta aceptada, aquí hay un resumen rápido de cómo codificar / decodificar una cadena a / desde base64 sin necesidad de una biblioteca.

str = "The quick brown fox jumps over the lazy dog";
b64 = btoa(unescape(encodeURIComponent(str)));
str = decodeURIComponent(escape(window.atob(b64)));

Manifestación

(usa la biblioteca jQuery, pero no para codificar / decodificar)

choque
fuente
Para confirmar, ¿esto admite caracteres UTF-8?
Crashalot
1
@Crashalot Me doy cuenta de que esto es dos años demasiado tarde, pero sí. También me estoy dando cuenta mientras escribo esto que proporcionó una edición que posiblemente hizo que UTF8 funcionara.
tycrek
Para cualquiera que esté buscando una buena solución para usar con Node.js, puedo confirmar que esto funciona. Para decodificar en Node, utilicé:Buffer.from(b64data, 'base64').toString();
tycrek
26

Hay un par de errores en ambas implementaciones de _utf8_decode. c1y c2se asignan como variables globales debido al uso incorrecto de la vardeclaración, y c3no se inicializa ni declara en absoluto.

Funciona, pero estas variables sobrescribirán las existentes con el mismo nombre fuera de esta función.

Aquí hay una versión que no hará esto:

// private method for UTF-8 decoding
_utf8_decode : function (utftext) {
    var string = "";
    var i = 0;
    var c = 0, c1 = 0, c2 = 0;

    while ( i < utftext.length ) {

        c = utftext.charCodeAt(i);

        if (c < 128) {
            string += String.fromCharCode(c);
            i++;
        }
        else if((c > 191) && (c < 224)) {
            c1 = utftext.charCodeAt(i+1);
            string += String.fromCharCode(((c & 31) << 6) | (c1 & 63));
            i += 2;
        }
        else {
            c1 = utftext.charCodeAt(i+1);
            c2 = utftext.charCodeAt(i+2);
            string += String.fromCharCode(((c & 15) << 12) | ((c1 & 63) << 6) | (c2 & 63));
            i += 3;
        }

    }
    return string;
}
Robbles
fuente
99
@Daan No tenía suficiente representante para editar las respuestas cuando escribí esta respuesta ... en 2011.
roba el
2
IE7? ¡Supongo que deberíamos dejar de perder el tiempo para escribir código para eso, la gente no dejará de usar esta vieja tecnología a menos que los desarrolladores los obliguemos a hacerlo!
Rami Dabain
@RonanDejhero ¿no funciona en IE7? No recuerdo si lo probé en ese navegador en particular.
Robbles
1
Lo que quise decir es que si no funciona en IE7, ¡a nadie debería importarle! no
probé
16

Hice +1 en la respuesta de Sunny, pero quería contribuir con algunos cambios que hice para mi propio proyecto en caso de que alguien lo encontrara útil. Básicamente, limpié un poco el código original para que JSLint no se queje tanto, e hice que los métodos marcados como privados en los comentarios fueran realmente privados. También agregué dos métodos que necesitaba en mi propio proyecto, a saber, decodeToHexy encodeFromHex.

El código:

var Base64 = (function() {
    "use strict";

    var _keyStr = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";

    var _utf8_encode = function (string) {

        var utftext = "", c, n;

        string = string.replace(/\r\n/g,"\n");

        for (n = 0; n < string.length; n++) {

            c = string.charCodeAt(n);

            if (c < 128) {

                utftext += String.fromCharCode(c);

            } else if((c > 127) && (c < 2048)) {

                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);

            } else {

                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);

            }

        }

        return utftext;
    };

    var _utf8_decode = function (utftext) {
        var string = "", i = 0, c = 0, c1 = 0, c2 = 0;

        while ( i < utftext.length ) {

            c = utftext.charCodeAt(i);

            if (c < 128) {

                string += String.fromCharCode(c);
                i++;

            } else if((c > 191) && (c < 224)) {

                c1 = utftext.charCodeAt(i+1);
                string += String.fromCharCode(((c & 31) << 6) | (c1 & 63));
                i += 2;

            } else {

                c1 = utftext.charCodeAt(i+1);
                c2 = utftext.charCodeAt(i+2);
                string += String.fromCharCode(((c & 15) << 12) | ((c1 & 63) << 6) | (c2 & 63));
                i += 3;

            }

        }

        return string;
    };

    var _hexEncode = function(input) {
        var output = '', i;

        for(i = 0; i < input.length; i++) {
            output += input.charCodeAt(i).toString(16);
        }

        return output;
    };

    var _hexDecode = function(input) {
        var output = '', i;

        if(input.length % 2 > 0) {
            input = '0' + input;
        }

        for(i = 0; i < input.length; i = i + 2) {
            output += String.fromCharCode(parseInt(input.charAt(i) + input.charAt(i + 1), 16));
        }

        return output;
    };

    var encode = function (input) {
        var output = "", chr1, chr2, chr3, enc1, enc2, enc3, enc4, i = 0;

        input = _utf8_encode(input);

        while (i < input.length) {

            chr1 = input.charCodeAt(i++);
            chr2 = input.charCodeAt(i++);
            chr3 = input.charCodeAt(i++);

            enc1 = chr1 >> 2;
            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            enc4 = chr3 & 63;

            if (isNaN(chr2)) {
                enc3 = enc4 = 64;
            } else if (isNaN(chr3)) {
                enc4 = 64;
            }

            output += _keyStr.charAt(enc1);
            output += _keyStr.charAt(enc2);
            output += _keyStr.charAt(enc3);
            output += _keyStr.charAt(enc4);

        }

        return output;
    };

    var decode = function (input) {
        var output = "", chr1, chr2, chr3, enc1, enc2, enc3, enc4, i = 0;

        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

        while (i < input.length) {

            enc1 = _keyStr.indexOf(input.charAt(i++));
            enc2 = _keyStr.indexOf(input.charAt(i++));
            enc3 = _keyStr.indexOf(input.charAt(i++));
            enc4 = _keyStr.indexOf(input.charAt(i++));

            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;

            output += String.fromCharCode(chr1);

            if (enc3 !== 64) {
                output += String.fromCharCode(chr2);
            }
            if (enc4 !== 64) {
                output += String.fromCharCode(chr3);
            }

        }

        return _utf8_decode(output);
    };

    var decodeToHex = function(input) {
        return _hexEncode(decode(input));
    };

    var encodeFromHex = function(input) {
        return encode(_hexDecode(input));
    };

    return {
        'encode': encode,
        'decode': decode,
        'decodeToHex': decodeToHex,
        'encodeFromHex': encodeFromHex
    };
}());
Joe Dyndale
fuente
Inicialmente pensé que su desenrollado de la concatenación de salida en declaraciones separadas sería más óptimo, pero después de pensarlo por un segundo, esto debería ser más ineficiente ya que las cadenas de Javascript son inmutables y causaría 4 copias de blobs de datos potencialmente enormes cuando funciona con grandes archivos de datos binarios. Es una apuesta más segura concatenar los 4 caracteres juntos primero y luego construir una nueva cadena. Ojalá supiera con certeza un mejor método de construcción de cadenas que seguramente sea eficiente en todas las plataformas. (incluso IE6)
Marius
No he considerado el rendimiento en mi limpieza del código publicado originalmente. Simplemente lo hice más legible e hice que los métodos marcados como privados en los comentarios en el original en realidad fueran privados usando el patrón de módulo revelador. Estoy seguro de que también se puede optimizar con respecto al rendimiento. No estoy seguro de cuándo comenzará la recolección de basura, y el hash de archivos grandes a través de Javascript no es muy común (o, de hecho, probablemente no sea la solución óptima en ningún caso).
Joe Dyndale
Es curioso cómo este código vive aquí. Ya hay 3 versiones diferentes en esta página.
gregn3
16

Para los navegadores más nuevos, codifique Uint8Array en cadena y decodifique la cadena en Uint8Array.

const base64 = {
    decode: s => Uint8Array.from(atob(s), c => c.charCodeAt(0)),
    encode: b => btoa(String.fromCharCode(...new Uint8Array(b)))
};

Para Node.js puede usar lo siguiente para codificar string, Buffer o Uint8Array a string, y decodificar de string, Buffer o Uint8Array a Buffer.

const base64 = {
    decode: s => Buffer.from(s, 'base64'),
    encode: b => Buffer.from(b).toString('base64')
};
Rix
fuente
13

Para hacer una URL de cadena codificada en Base64 amigable, en JavaScript podría hacer algo como esto:

// if this is your Base64 encoded string
var str = 'VGhpcyBpcyBhbiBhd2Vzb21lIHNjcmlwdA=='; 

// make URL friendly:
str = str.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, '');

// reverse to original encoding
str = (str + '===').slice(0, str.length + (str.length % 4));
str = str.replace(/-/g, '+').replace(/_/g, '/');

Vea también este violín: http://jsfiddle.net/magikMaker/7bjaT/

magikMaker
fuente
99
Sugeriría humildemente que el uso de encodeURIComponentpuede resultar en un resultado superior con menos gasto de esfuerzo por parte del desarrollador.
Pablo Fernández
11
encodeURIComponent cambiará la longitud de las cadenas codificadas en base64, y reemplazar '-' y '_' con '+' y '/' es una práctica estándar cuando se usa base64 en URL (p. ej. docs.python.org/library/base64.html#base64 .urlsafe_b64encode ). No hay necesidad de enojarse.
natevw
12

Tenga en cuenta que esto no es adecuado para cadenas Unicode sin procesar. Vea la sección Unicode aquí .

Sintaxis para codificar

var encodedData = window.btoa(stringToEncode);

Sintaxis para decodificar

var decodedData = window.atob(encodedData);

Kathir
fuente
1
Enlace directo a la sección Unicode: developer.mozilla.org/en-US/docs/Web/API/…
TomTasche
12

He reescrito a mano, estos métodos de codificación y decodificación con la excepción del hexadecimal en un formato modular para compatibilidad multiplataforma / navegador y también con alcance privado real, y usos btoayatob si existen debido a la velocidad en lugar de utilizar su propia codificación:

https://gist.github.com/Nijikokun/5192472

Uso:

base64.encode(/* String */);
base64.decode(/* String */);

utf8.encode(/* String */);
utf8.decode(/* String */);
Nijikokun
fuente
12

Esta pregunta y sus respuestas me indicaron la dirección correcta.
Especialmente con unicode atob y btoa no se puede usar "vainilla" y en estos días TODO es unicode ..

Directamente desde Mozilla, dos buenas funciones para este propósito (probado con etiquetas Unicode y HTML dentro)

function b64EncodeUnicode(str) {
    return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
        return String.fromCharCode('0x' + p1);
    }));
}

b64EncodeUnicode('✓ à la mode'); // "4pyTIMOgIGxhIG1vZGU="
b64EncodeUnicode('\n'); // "Cg=="



function b64DecodeUnicode(str) {
    return decodeURIComponent(Array.prototype.map.call(atob(str), function(c) {
        return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
    }).join(''));
}

b64DecodeUnicode('4pyTIMOgIGxhIG1vZGU='); // "✓ à la mode"
b64DecodeUnicode('Cg=='); // "\n"

Estas funciones realizarán un rayo rápido en comparación con la decodificación de base64 sin formato utilizando una función de JavaScript personalizada ya que btoa y atob se ejecutan fuera del intérprete.

Si puede ignorar el viejo IE y los viejos teléfonos móviles (¿como el iPhone 3?), Esta debería ser una buena solución.

Juan
fuente
10

Si necesita codificar un objeto de imagen HTML, puede escribir funciones simples como:

function getBase64Image(img) {  
  var canvas = document.createElement("canvas");  
  canvas.width = img.width;  
  canvas.height = img.height;  
  var ctx = canvas.getContext("2d");  
  ctx.drawImage(img, 0, 0);  
  var dataURL = canvas.toDataURL("image/png");  
  // escape data:image prefix
  return dataURL.replace(/^data:image\/(png|jpg);base64,/, "");  
  // or just return dataURL
  // return dataURL
}  

Para obtener la base64 de la imagen por id:

function getBase64ImageById(id){  
  return getBase64Image(document.getElementById(id));  
} 

mas aqui

Nedudi
fuente
Sí, y var img = nueva imagen (); img.src = "../images/myPic.png";
pdschuller
7

Contribuyendo con un polyfill minificado para window.atob+ window.btoaque estoy usando actualmente.

(function(){function t(t){this.message=t}var e="undefined"!=typeof exports?exports:this,r="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";t.prototype=Error(),t.prototype.name="InvalidCharacterError",e.btoa||(e.btoa=function(e){for(var o,n,a=0,i=r,c="";e.charAt(0|a)||(i="=",a%1);c+=i.charAt(63&o>>8-8*(a%1))){if(n=e.charCodeAt(a+=.75),n>255)throw new t("'btoa' failed: The string to be encoded contains characters outside of the Latin1 range.");o=o<<8|n}return c}),e.atob||(e.atob=function(e){if(e=e.replace(/=+$/,""),1==e.length%4)throw new t("'atob' failed: The string to be decoded is not correctly encoded.");for(var o,n,a=0,i=0,c="";n=e.charAt(i++);~n&&(o=a%4?64*o+n:n,a++%4)?c+=String.fromCharCode(255&o>>(6&-2*a)):0)n=r.indexOf(n);return c})})();
Johan
fuente
6

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
6

Aquí hay una versión de AngularJS Factory de la de @ user850789:

'use strict';

var ProjectNameBase64Factory = angular.module('project_name.factories.base64', []);

ProjectNameBase64Factory.factory('Base64', function () {
    var Base64 = {
        // private property
        _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

        // public method for encoding
        encode: function (input) {
            var output = "";
            var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
            var i = 0;

            input = Base64._utf8_encode(input);

            while (i < input.length) {

                chr1 = input.charCodeAt(i++);
                chr2 = input.charCodeAt(i++);
                chr3 = input.charCodeAt(i++);

                enc1 = chr1 >> 2;
                enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                enc4 = chr3 & 63;

                if (isNaN(chr2)) {
                    enc3 = enc4 = 64;
                } else if (isNaN(chr3)) {
                    enc4 = 64;
                }

                output = output +
                         Base64._keyStr.charAt(enc1) + Base64._keyStr.charAt(enc2) +
                         Base64._keyStr.charAt(enc3) + Base64._keyStr.charAt(enc4);

            }

            return output;
        },

        // public method for decoding
        decode: function (input) {
            var output = "";
            var chr1, chr2, chr3;
            var enc1, enc2, enc3, enc4;
            var i = 0;

            input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

            while (i < input.length) {

                enc1 = Base64._keyStr.indexOf(input.charAt(i++));
                enc2 = Base64._keyStr.indexOf(input.charAt(i++));
                enc3 = Base64._keyStr.indexOf(input.charAt(i++));
                enc4 = Base64._keyStr.indexOf(input.charAt(i++));

                chr1 = (enc1 << 2) | (enc2 >> 4);
                chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
                chr3 = ((enc3 & 3) << 6) | enc4;

                output = output + String.fromCharCode(chr1);

                if (enc3 != 64) {
                    output = output + String.fromCharCode(chr2);
                }
                if (enc4 != 64) {
                    output = output + String.fromCharCode(chr3);
                }

            }

            output = Base64._utf8_decode(output);

            return output;

        },

        // private method for UTF-8 encoding
        _utf8_encode: function (string) {
            string = string.replace(/\r\n/g, "\n");
            var utftext = "";

            for (var n = 0; n < string.length; n++) {

                var c = string.charCodeAt(n);

                if (c < 128) {
                    utftext += String.fromCharCode(c);
                }
                else if ((c > 127) && (c < 2048)) {
                    utftext += String.fromCharCode((c >> 6) | 192);
                    utftext += String.fromCharCode((c & 63) | 128);
                }
                else {
                    utftext += String.fromCharCode((c >> 12) | 224);
                    utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                    utftext += String.fromCharCode((c & 63) | 128);
                }

            }

            return utftext;
        },

        // private method for UTF-8 decoding
        _utf8_decode: function (utftext) {
            var string = "";
            var i = 0;
            var c = 0, c2 = 0, c3 = 0;

            while (i < utftext.length) {

                c = utftext.charCodeAt(i);

                if (c < 128) {
                    string += String.fromCharCode(c);
                    i++;
                }
                else if ((c > 191) && (c < 224)) {
                    c2 = utftext.charCodeAt(i + 1);
                    string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                    i += 2;
                }
                else {
                    c2 = utftext.charCodeAt(i + 1);
                    c3 = utftext.charCodeAt(i + 2);
                    string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                    i += 3;
                }

            }
            return string;
        }
    };
    return Base64;
});
A
fuente
6

Necesitaba la codificación de una cadena UTF-8 como base64 para un proyecto mío. La mayoría de las respuestas aquí no parecen manejar adecuadamente los pares sustitutos de UTF-16 cuando se convierten a UTF-8, por lo que, para completar, publicaré mi solución:

function strToUTF8Base64(str) {

    function decodeSurrogatePair(hi, lo) {
        var resultChar = 0x010000;
        resultChar += lo - 0xDC00;
        resultChar += (hi - 0xD800) << 10;
        return resultChar;
    }

    var bytes = [0, 0, 0];
    var byteIndex = 0;
    var result = [];

    function output(s) {
        result.push(s);
    }

    function emitBase64() {

        var digits =
                'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +
                'abcdefghijklmnopqrstuvwxyz' +
                '0123456789+/';

        function toDigit(value) {
            return digits[value];
        }

        // --Byte 0--    --Byte 1--    --Byte 2--
        // 1111  1122    2222  3333    3344  4444

        var d1 = toDigit(bytes[0] >> 2);
        var d2 = toDigit(
            ((bytes[0] & 0x03) << 4) |
            (bytes[1] >> 4));
        var d3 = toDigit(
            ((bytes[1] & 0x0F) << 2) |
            (bytes[2] >> 6));
        var d4 = toDigit(
            bytes[2] & 0x3F);

        if (byteIndex === 1) {
            output(d1 + d2 + '==');
        }
        else if (byteIndex === 2) {
            output(d1 + d2 + d3 + '=');
        }
        else {
            output(d1 + d2 + d3 + d4);
        }
    }

    function emit(chr) {
        bytes[byteIndex++] = chr;
        if (byteIndex == 3) {
            emitBase64();
            bytes[0] = 0;
            bytes[1] = 0;
            bytes[2] = 0;
            byteIndex = 0;
        }
    }

    function emitLast() {
        if (byteIndex > 0) {
            emitBase64();
        }
    }

    // Converts the string to UTF8:

    var i, chr;
    var hi, lo;
    for (i = 0; i < str.length; i++) {
        chr = str.charCodeAt(i);

        // Test and decode surrogate pairs in the string
        if (chr >= 0xD800 && chr <= 0xDBFF) {
            hi = chr;
            lo = str.charCodeAt(i + 1);
            if (lo >= 0xDC00 && lo <= 0xDFFF) {
                chr = decodeSurrogatePair(hi, lo);
                i++;
            }
        }

        // Encode the character as UTF-8.
        if (chr < 0x80) {
            emit(chr);
        }
        else if (chr < 0x0800) {
            emit((chr >> 6) | 0xC0);
            emit(((chr >> 0) & 0x3F) | 0x80);
        }
        else if (chr < 0x10000) {
            emit((chr >> 12) | 0xE0);
            emit(((chr >>  6) & 0x3F) | 0x80);
            emit(((chr >>  0) & 0x3F) | 0x80);
        }
        else if (chr < 0x110000) {
            emit((chr >> 18) | 0xF0);
            emit(((chr >> 12) & 0x3F) | 0x80);
            emit(((chr >>  6) & 0x3F) | 0x80);
            emit(((chr >>  0) & 0x3F) | 0x80);
        }
    }

    emitLast();

    return result.join('');
}

Tenga en cuenta que el código no se prueba a fondo. Probé algunas entradas, incluidas cosas comostrToUTF8Base64('衠衢蠩蠨') y comparé con la salida de una herramienta de codificación en línea ( https://www.base64encode.org/ ).

Ricardo Machado
fuente
5

Para mi proyecto todavía necesito soportar IE7 y trabajar con una gran entrada para codificar.

Basado en el código propuesto por Joe Dyndale y como se sugiere en el comentario de Marius, es posible mejorar el rendimiento con IE7 construyendo el resultado con una matriz en lugar de una cadena.

Aquí está el ejemplo para codificar:

var encode = function (input) {
    var output = [], chr1, chr2, chr3, enc1, enc2, enc3, enc4, i = 0;

    input = _utf8_encode(input);

    while (i < input.length) {

        chr1 = input.charCodeAt(i++);
        chr2 = input.charCodeAt(i++);
        chr3 = input.charCodeAt(i++);

        enc1 = chr1 >> 2;
        enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
        enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
        enc4 = chr3 & 63;

        if (isNaN(chr2)) {
            enc3 = enc4 = 64;
        } else if (isNaN(chr3)) {
            enc4 = 64;
        }

        output.push(_keyStr.charAt(enc1));
        output.push(_keyStr.charAt(enc2));
        output.push(_keyStr.charAt(enc3));
        output.push(_keyStr.charAt(enc4));

    }

    return output.join("");
};
Nico
fuente
5

Si bien un poco más de trabajo, si desea una solución nativa de alto rendimiento, hay algunas funciones HTML5 que puede usar.

Si puede obtener sus datos en un Blob, entonces puede usar la función FileReader.readAsDataURL () para obtener undata:// URL y cortar el frente para obtener los datos de base64.

Sin embargo, es posible que deba realizar un procesamiento adicional para codificar los datos, ya que no estoy seguro de si los +caracteres se escapan o no para la data://URL, pero esto debería ser bastante trivial.

Malvinoso
fuente
5

Bueno, si está usando dojo, nos da una forma directa de codificar o decodificar en base64.

Prueba esto:-

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);
Vikash Pandey
fuente
3

Puedes usar window.btoay window.atob...

const encoded = window.btoa('Alireza Dezfoolian'); // encode a string
const decoded = window.atob(encoded); // decode the string

Probablemente, usar la forma en que MDN puede hacer su trabajo mejor ... También acepta unicode ... usando estas dos funciones simples:

// ucs-2 string to base64 encoded ascii
function utoa(str) {
    return window.btoa(unescape(encodeURIComponent(str)));
}
// base64 encoded ascii to ucs-2 string
function atou(str) {
    return decodeURIComponent(escape(window.atob(str)));
}
// Usage:
utoa('✓ à la mode'); // 4pyTIMOgIGxhIG1vZGU=
atou('4pyTIMOgIGxhIG1vZGU='); // "✓ à la mode"

utoa('I \u2661 Unicode!'); // SSDimaEgVW5pY29kZSE=
atou('SSDimaEgVW5pY29kZSE='); // "I ♡ Unicode!"
Alireza
fuente
3

Aquí hay una demostración en vivo de JS atob()y btoa()funciones incorporadas:

<!DOCTYPE html>
<html>
  <head>
    <style>
      textarea{
        width:30%;
        height:100px;
      }
    </style>
    <script>
      // encode string to base64
      function encode()
      {
        var txt = document.getElementById("txt1").value;
        var result = btoa(txt);
        document.getElementById("txt2").value = result;
      }
      // decode base64 back to original string
      function decode()
      {
        var txt = document.getElementById("txt3").value;
        var result = atob(txt);
        document.getElementById("txt4").value = result;
      }
    </script>
  </head>
  <body>
    <div>
      <textarea id="txt1">Some text to decode
      </textarea>
    </div>
    <div>
      <input type="button" id="btnencode" value="Encode" onClick="encode()"/>
    </div>
    <div>
      <textarea id="txt2">
      </textarea>
    </div>
    <br/>
    <div>
      <textarea id="txt3">U29tZSB0ZXh0IHRvIGRlY29kZQ==
      </textarea>
    </div>
    <div>
      <input type="button" id="btndecode" value="Decode" onClick="decode()"/>
    </div>
    <div>
      <textarea id="txt4">
      </textarea>
    </div>
  </body>
</html>
jonathana
fuente
2

Utilice la biblioteca js-base64 como

btoa () no funciona con emojis

var str = "I was funny 😂";
console.log("Original string:", str);

var encodedStr = Base64.encode(str)
console.log("Encoded string:", encodedStr);

var decodedStr = Base64.decode(encodedStr)
console.log("Decoded string:", decodedStr);
<script src="https://cdn.jsdelivr.net/npm/[email protected]/base64.min.js"></script>

Shivaji Mutkule
fuente