Кодування та декодування Base64 в JavaScript на стороні клієнта


270

Чи є в JavaScript методи, які можна використовувати для кодування та декодування рядка за допомогою кодування base64?



Відповіді:


214

Деякі веб-переглядачі, такі як Firefox, Chrome, Safari, Opera та IE10 +, можуть працювати з Base64 на власній основі. Погляньте на це питання про Стакковерф . Це використання btoa()та atob()функції .

Для сервера JavaScript (Node), ви можете використовувати Buffers для декодування.

Якщо ви збираєтеся вирішувати крос-браузер, існують такі бібліотеки, як CryptoJS або такий код:

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

З останнім потрібно ретельно перевірити функціональність на сумісність між веб-переглядачами. І про помилку вже повідомлялося .


1
Я використовував цей метод для кодування SVG в base64 зі схемою URI дати. Подив: ця функція urlencodes кожен символу, так що я отримую цей некоректний XML на цілі:% 3C% 3Fxml% 20version% 3D% 271,0% 27% 20% 3F% 3E% 3Csvg% 20xmlns% 3D% 27http% ...
Dereckson

21
Node.js може робити Base64 споконвічно: new Buffer('Hello, world!').toString('base64'); new Buffer('SGVsbG8sIHdvcmxkIQ==', 'base64').toString('ascii'); (джерело)
nyuszika7h

І якщо я хотів зробити це безпечним для URL-адрес?
anddero

1
у вузлі 5+ використовуйте Buffer.from, як new Buffer(string)застаріле. Buffer.from(jwt.split('.')[1], 'base64').toString()
Рей Фосс

63

У браузерах на базі Gecko / WebKit (Firefox, Chrome і Safari) та Opera можна використовувати btoa () та atob () .

Оригінальна відповідь: Як можна кодувати рядок до Base64 в JavaScript?


Це рятівник життя. Я використовував кілька різних реалізацій для декодування дуже великих рядків, закодованих base64, і результат завжди був неправильним. atob () чудово працює!
b2238488

15
Маленький нітпік: Opera не заснована на Gecko чи Webkit, вона використовує власну систему візуалізації під назвою Presto.
Пітер Олсон

Нічого, дякую за це. Не знав, що в цих браузерах є нативний кодер base64!
Роб Портер

5
@PeterOlson Більше не :)
Мустафа

1
Я розумію, що це стара публікація, але щодо проблеми @ b2238488 ви можете розділити рядок base64 так, щоб довжина кожного маркера була кратною 4, і розшифрувати їх окремо. Результат буде таким самим, як декодування всієї строки одразу.
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!"

Крос-браузер

Перезаписані та модульовані UTF-8 та Base64 Javascript кодування та декодування бібліотек / модулів для AMD, CommonJS, Nodejs та браузерів. Сумісний веб-браузер.


з Node.js

Ось як ви кодуєте звичайний текст до base64 в 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');

І ось, як ви декодуєте рядки, закодовані base64:

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

з Dojo.js

Для кодування масиву байтів за допомогою dojox.encoding.base64:

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

Для розшифровки рядка, кодованого base64:

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

установка котловану кутова-основа64

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

Але як?

Якщо ви хочете дізнатися більше про те, як кодується base64 загалом, і зокрема в JavaScript, я рекомендую цю статтю: Інформатика в JavaScript: кодування Base64


1
FYI: версія крос-браузер має деякі неприємні витік c2і , ймовірно , c1і c3тому він не буде працювати з "use strict"такими , як визначено вище.
Кемпбелн

41

Ось затягнута версія посту Снайпера. Він передбачає добре сформовану рядок base64 без повернення каретки. Ця версія усуває пару циклів, додає &0xffвиправлення від Ярослава, виключає задні нулі, плюс трохи коду гольфу.

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

5
Ще менше байт; 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

Так, але працює лише з ASCII. Наприклад, персонажів Cyr зловживають.
Мартін Ковачев

@MartinKovachev Ви можете опублікувати новий коментар із прикладом тексту із символами Cyr та відповідним кодування base64? Можливо, ми можемо виправити код, щоб вмістити його.
broc.seib

Ось: щось подібне: тестова фраза
Мартін Ковачев

2
@OliverSalzburg Ще менше генеруємо таблицю коду :):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++;
Майк

34

Коротка та швидка функція декодування Base64 без помилок:

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

6
Opera 11.62, здається, має проблеми з частиною '% 256'. Якщо замінити його на "& 0xff", це змусить його працювати.
Ярослав Ставничий

Код JavaScript JavaScript віртуальної кінцевої точки має проблеми з частиною '\ x00'. Замінивши його на r = r.replace (/ \ x00 / g, ''), змусить його працювати
Panupong Kongarn


11

Проект php.js має реалізацію JavaScript багатьох функцій PHP. base64_encodeі base64_decodeвключаються.


php.js - це втілення всього зла і належить до власного шару в пеклі. Уникайте його, як чуми. (Детальніше: softwareengineering.stackexchange.com/questions/126671/… )
Coreus

Я не бачу, що дуже підтримує це твердження про бланк у цьому посиланні, @ Корей. Використовується розсудливо, або як вихідний пункт, це цілком прийнятний спосіб з'ясувати еквівалентну логіку в JS для чогось, що ви, можливо, вже знаєте, як робити в PHP.
ceejayoz

8

Хтось сказав код гольфу? =)

Далі йде моя спроба покращити свій гандикап, поки не встигаючи за часом. Поставляється для вашої зручності.

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

Те, про що я був насправді, - це асинхронна реалізація, і, на мій подив, виявляється, forEachна відміну від $([]).eachреалізації методу JQuery - це дуже синхронно.

Якщо ви також мали на увазі такі шалені уявлення, затримка 0 window.setTimeoutзапустить асинхронно декодування base64 і виконає функцію зворотного виклику з результатом після завершення.

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

@Toothbrush запропонував "індексувати рядок, як масив", і позбутися від split. Ця рутина здається дійсно дивною, і не впевнений, наскільки вона буде сумісною, але вона потрапляє на іншу пташку, тому дозволяє її мати.

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

Намагаючись знайти додаткову інформацію про рядок JavaScript як масив, я натрапив на цю підказку, використовуючи /./gрегулярний вираз, щоб перейти через рядок. Це ще більше зменшує розмір коду, замінюючи рядок на місці та позбавляючи від необхідності збереження змінної повернення.

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

Якщо ви шукали щось трохи більш традиційне, можливо, наступне на ваш смак.

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

У мене не було проблеми із затримкою нуля, тому це було видалено, щоб залишатися за номіналом, але його слід легко вирішити за допомогою a trim()або a, trimRight()якщо ви хочете, якщо це створюватиме проблему для вас.

тобто.

return r.trimRight();

Примітка:

Результатом є рядок байтів ascii, якщо вам потрібно unicode, найпростіше - escapeце байт-рядок, який потім може бути розшифрований decodeURIComponentдля отримання рядка unicode.

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

Оскільки escapeце застаріло, ми можемо змінити нашу функцію, щоб підтримувати Unicode безпосередньо без необхідності, escapeабо String.fromCharCodeми можемо створити %рядок, що вийшов, готовий до декодування 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!


3
Вам не потрібно робити splitрядок, оскільки ви можете проіндексувати рядок JavaScript, як масив. s.split('').forEach(function ...можна замінити на [].forEach.call(s, function .... Це повинно бути набагато швидше, через це не потрібно розділяти рядок.
Зубна щітка

"Більш традиційний" був для мене повністю розбитий - створив шалений хаос зі слідами оригінального тексту, посипаного по всьому. На Chrome.
Стів Беннетт

@Steve Bennett Я протестував всі варіанти в хромі ... це працює. Чи можете ви навести приклад рядка base64, який не працює?
nickl-

Через три роки? Ха. Я навіть не пам'ятаю, для чого мені це було потрібно.
Стів Беннетт

6

Я спробував підпрограми Javascript на phpjs.org, і вони добре працювали.

Я вперше спробував підпрограми, запропоновані у вибраній відповіді Ранхіру Курай - http://ntt.cc/2008/01/19/base64-encoder-decoder-with-javascript.html

Я виявив, що вони працюють не за будь-яких обставин. Я написав тестовий випадок, коли ці підпрограми не вдається, і опублікував їх у GitHub за адресою:

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

Я також розмістив коментар до публікації в блозі на ntt.cc, щоб попередити автора (чекаю модерації - стаття стара, тому не впевнений, чи буде розміщений коментар).


1

Я вважаю за краще використовувати методи кодування / декодування bas64 від CryptoJS , найпопулярнішої бібліотеки для стандартних та захищених криптографічних алгоритмів, реалізованих у JavaScript, використовуючи кращі практики та шаблони.


1

У Node.js ми можемо це зробити простим способом

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

console.log(base64_decode); // "Hello World"

0

Для каркасів JavaScripts, де немає atobметоду, і якщо ви не хочете імпортувати зовнішні бібліотеки, це коротка функція, яка це робить.

Він отримає рядок, що містить кодоване значення Base64 і поверне декодований масив байтів (де масив байтів представлений як масив чисел, де кожне число є цілим числом від 0 до 255 включно).

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

Використовувані ресурси: RFC-4648 Розділ 4

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.