رمزگذاری و رمزگشایی Base64 در جاوااسکریپت سمت مشتری
آیا در جاوااسکریپت متدهایی وجود دارند که بتوان از آنها برای رمزگذاری و رمزگشایی یک رشته با استفاده از کدگذاری Base64 استفاده کرد؟
پاسخ شماره ۱
برخی مرورگرها مانند فایرفاکس، کروم، سافاری، اپرا و IE10+ میتوانند Base64 را بهطور بومی پردازش کنند. نگاهی به این سوال در استکاورفلو بیندازید. این از توابع btoa() و atob() استفاده میکند.
برای جاوا اسکریپت سمت سرور (Node)، می توانید از Buffers برای رمزگشایی استفاده کنید.
اگر به دنبال یک راهحل سازگار با مرورگرهای مختلف هستید، کتابخانههای موجودی مانند CryptoJS یا کدی مانند:
http://ntt.cc/2008/01/19/base64-encoder-decoder-with-javascript.html (آرشیو)
با دومی، شما نیاز دارید که عملکرد را به طور کامل برای سازگاری با مرورگرهای مختلف آزمایش کنید. و خطایی قبلاً گزارش شده است.
پاسخ شماره ۲
اینترنت اکسپلورر ۱۰+
// 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!"
Cross-Browser
کتابخانهها/ماژولهای کدنویسی و رمزگشایی UTF-8 و Base64 جاوااسکریپت دوباره نوشته شده و ماژولار برای 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 = Buffer.from('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 = Buffer.from('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)
bower نصب 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=');
}]);
اما چگونه؟
اگر میخواهید بیشتر درباره نحوه کدگذاری base64 به طور کلی و بهویژه در جاوااسکریپت بدانید، این مقاله را پیشنهاد میکنم: علوم کامپیوتر در جاوااسکریپت: کدگذاری Base64
پاسخ شماره ۳
در مرورگرهای مبتنی بر Gecko/WebKit (فایرفاکس، کروم و سافاری) و اپرا، میتوانید از btoa() و atob() استفاده کنید.
پاسخ اصلی: چگونه میتوانید یک رشته را در جاوااسکریپت به Base64 رمزگذاری کنید؟
پاسخ شماره ۴
در اینجا نسخهای فشرده شده از پست تک تیرانداز ارائه شده است. این نسخه فرض میکند که رشته 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;
};
پاسخ شماره ۵
تابع کوتاه و سریع دیکد 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;
}
پاسخ شماره ۶
مرورگرهای مدرن دارای توابع جاوااسکریپت داخلی برای رمزگذاری Base64 با تابع btoa() و رمزگشایی با تابع atob() هستند. اطلاعات بیشتر درباره پشتیبانی در نسخههای قدیمیتر مرورگرها: https://caniuse.com/?search=atob
با این حال، توجه داشته باشید که توابع atob و btoa تنها برای مجموعه کاراکتر ASCII کار میکنند. اگر به توابع Base64 برای مجموعه کاراکتر UTF-8 نیاز دارید، میتوانید این کار را با استفاده از:
function base64_encode(s) {
return btoa(unescape(encodeURIComponent(s)));
}
function base64_decode(s) {
return decodeURIComponent(escape(atob(s)));
}
پاسخ شماره ۷
function b64_to_utf8( str ) {
return decodeURIComponent(escape(window.atob( str )));
}https://developer.mozilla.org/fa/docs/Web/API/WindowBase64/Base64_encoding_and_decoding#The_.22Unicode_Problem.22
پاسخ شماره ۸
کسی گفت گلف کد؟ =)
در ادامه تلاش من برای بهبود مهارتم در حالی که با زمانه همگام میشوم آمده است. برای راحتی شما ارائه شده است.
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 کاملاً همزمان است.
اگر شما هم چنین تصورات عجیب و غریبی در ذهن داشتید، یک window.setTimeout با تأخیر 0، تابع decode base64 را به صورت غیرهمزمان اجرا میکند و پس از اتمام، تابع callback را با نتیجه اجرا میکند.
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;
}
در حالی که تلاش میکردم اطلاعات بیشتری درباره رشتههای جاوااسکریپت بهعنوان آرایه پیدا کنم، به این نکته حرفهای برخورد کردم که از یک /./g regex برای قدم زدن در یک رشته استفاده میکند. این باعث کاهش بیشتر اندازه کد میشود زیرا رشته را در محل جایگزین میکند و نیاز به نگه داشتن یک متغیر بازگشتی را از بین میبرد.
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;
}
من مشکل null باقیمانده نداشتم بنابراین این حذف شد تا کمتر از حد معمول باقی بماند، اما این به راحتی با trim() یا trimRight() قابل حل است اگر ترجیح دهید، اگر این برای شما مشکلی ایجاد کند.
یعنی
return r.trimRight();
توجه :
نتیجه یک رشته بایت اسکی است، اگر به یونیکد نیاز دارید، سادهترین راه این است که رشته بایت را با escape پردازش کنید و سپس با decodeURIComponent آن را رمزگشایی کنید تا رشته یونیکد به دست آید.
function decode_base64_usc(s) {
return decodeURIComponent(escape(decode_base64(s)));
}
از آنجایی که escape در حال منسوخ شدن است، میتوانیم تابع خود را تغییر دهیم تا مستقیماً از یونیکد پشتیبانی کند بدون نیاز به 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);
}));
}
ویرایش برای @Charles Byrne:
نمیتوانم به یاد بیاورم که چرا کاراکترهای پرکننده '=' را نادیده نگرفتیم، ممکن است با یک مشخصهای کار کردهایم که در آن زمان نیاز به آنها نداشت. اگر بخواهیم رویه decodeURIComponent را طوری تغییر دهیم که این کاراکترها را نادیده بگیرد، همانطور که باید چون آنها نماینده هیچ دادهای نیستند، نتیجه مثال را به درستی رمزگشایی میکند.
function decode_base64(s) {
var b=l=0,
m='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
return decodeURIComponent(s.replace(/=*$/,'').replace(/./g, function (v) {
b=(b<<6)+m.indexOf(v); l+=6;
return l<8?'':'%'+(0x100+((b>>>(l-=8))&0xff)).toString(16).slice(-2);
}));
}
اکنون با فراخوانی decode_base64('4pyTIMOgIGxhIG1vZGU=') رشته کدگذاری شده '✓ à la mode' را بدون هیچ خطایی برمی گرداند.
از آنجا که '=' به عنوان کاراکتر پرکننده رزرو شده است، میتوانم ناتوانی خود در کد گلف را کاهش دهم، اگر اجازه داشته باشم:
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||'='==v?'':'%'+(0x100+((b>>>(l-=8))&0xff)).toString(16).slice(-2);
}));
}
لذت ببر!
پاسخ شماره ۹
پروژه php.js پیادهسازیهای جاوااسکریپتی بسیاری از توابع PHP را دارد. base64_encode و base64_decode نیز شامل شدهاند.
پاسخ شماره ۱۰
در هر صورت، ارزش آن هرچه باشد، من از پاسخcهای دیگر الهام گرفتم و ابزار کوچکی نوشتم که APIهای پلتفرم خاصی را فراخوانی میcکند تا از Node.js یا مرورگر در سطح جهانی استفاده شوند:
/**
* Encode a string of text as base64
*
* @param data The string of text.
* @returns The base64 encoded string.
*/
function encodeBase64(data: string) {
if (typeof btoa === "function") {
return btoa(data);
} else if (typeof Buffer === "function") {
return Buffer.from(data, "utf-8").toString("base64");
} else {
throw new Error("Failed to determine the platform specific encoder");
}
}
/**
* Decode a string of base64 as text
*
* @param data The string of base64 encoded text
* @returns The decoded text.
*/
function decodeBase64(data: string) {
if (typeof atob === "function") {
return atob(data);
} else if (typeof Buffer === "function") {
return Buffer.from(data, "base64").toString("utf-8");
} else {
throw new Error("Failed to determine the platform specific decoder");
}
}