क्या आपको Base64 फ़ॉर्मेट के साथ काम करना होता है? तो यह साइट आपके लिए एकदम सही है! अपने डेटा को एन्कोड या डिकोड करने के लिए, हमारे बेहद आसान ऑनलाइन टूल का इस्तेमाल करें।

JavaScript/Browser में Base64 एन्कोडिंग: एक सम्पूर्ण गाइड

आपके पास कोई चीज़ है जिसे सफ़र करना है, और रास्ता बस सादे ASCII के लिए ही चौड़ा है। वह JSON रिस्पॉन्स के अंदर आने वाली छवि हो, URL में सवार होने वाली कॉन्फ़िग ऑब्जेक्ट हो, ऐसा टोकन जिसके तीन सेगमेंट डॉट और अक्षरों से बने हों, या ऐसी फ़ाइल जो API के हिसाब से JSON बॉडी के अंदर Base64 स्ट्रिंग के रूप में ही आनी चाहिए। Base64 इसी हालत का टोल-बूथ है, और इस साइट का होम पेज इस फ़ॉर्मैट को पहले से समझा चुका है - हर तीन बाइट की जगह खड़े चार प्रिंटेबल चर, और ग्रुप को पूरा करने के लिए = पैडिंग - तो पढ़ते समय दिमाग़ में यह एक ही नंबर रखें: एन्कोडिंग वह दिशा है जिसमें डेटा बढ़ता है। हर तीन बाइट जो आप सौंपते हैं, चार चर बनकर लौटते हैं - करीब 33 फ़ीसदी की साइज़ का कर, जो बैंडविड्थ, स्टोरेज, और मेमोरी में वसूला जाता है। जब चैनल प्रिंटेबल टेक्स्ट माँगता हो तब Base64 इस्तेमाल करें, और ठीक-ठीक जानें कि वह कर आपको क्या ख़र्च करवाता है।

हौसला बढ़ाने वाली बात यह है कि ब्राउज़र यह काम एक पैकेज के बिना भी हमेशा कर सकता रहा है। btoa() 2000 के दशक की शुरुआत से शिप हो रहा है, TextEncoder ने एक दशक पहले आपके असली Unicode टेक्स्ट को साफ़-सुथरे बाइट्स में बदलना शुरू कर दिया, और Baseline 2025 की लहर में प्लेटफ़ॉर्म ने आख़िरकार Uint8Array.toBase64() जोड़ा, जो बाइट ऐरे को सीधे एन्कोड करता है, URL-सुरक्षित वर्णमाला के ऑप्शन के साथ। यह लेख वह निर्णय-नक्शा है: किस काम के लिए कौन सा औज़ार, तीखे किनारे कहाँ छुपे हैं (वे सब उसी एक सीमा तक लौटते हैं), और उन जगहों के लिए कन्क्री रेसिपी जहाँ आपको वाक़ई Base64 बनवाना होगा।

अपना एन्कोडर चुनें

अब सिर्फ़ एक "वही" एन्कोडर नहीं रहा, और ग़लत वाला पकड़ना ही क्लासिक बगों की जन्म-गाँठ है। नीचे दी गई टेबल पूरा निर्णय-वृक्ष है:

हाल-ए-वाक़िया क्या पकड़ें
सादा ASCII टेक्स्ट, एक बार की वैल्यू btoa(text)
एक्सेंट, इमोजी, CJK वाला असली टेक्स्ट new TextEncoder().encode(text), फिर btoa या toBase64
बाइट्स पहले से Uint8Array में 2025+ ब्राउज़रों में bytes.toBase64(), बाक़ी जगह चंकड btoa पुल
URLs, JWTs, फ़ाइल-नाम toBase64({ alphabet: 'base64url', omitPadding: true })
पुराने ब्राउज़र या साझा कोडबेस js-base64, या क्लासिक TextEncoder + btoa रेसिपी

टेबल के नीचे का पैटर्न: btoa() सिर्फ़ सिंगल-बाइट चर पढ़ता है, इसलिए जो कुछ भी ASCII नहीं है, उसे पहले बाइट ऐरे बनना पड़ता है, और वही बाइट ऐरे वह चीज़ है जिसके चारों ओर आधुनिक API बने हैं। "टेक्स्ट बाइट्स बनता है, बाइट्स Base64 बनते हैं" - इसे दिमाग़ में रख लें, और इस लेख की हर रेसिपी वही दो कदम हैं, बस नाम अलग के हैं।

btoa और Latin1 की सीमा

btoa(stringToEncode) - बाइनरी स्ट्रिंग से ASCII स्ट्रिंग - मूल एन्कोडर है, हर उस ब्राउज़र में उपलब्ध जो मायने रखता है (Chrome 4, Firefox 1, Safari 3, IE 10 और उसके बाद, सभी वर्कर स्कोप्स, और Node वर्ज़न 16 से)। इसके करार में एक ही शर्त है, और हर मुसीबत वही शर्त शुरू करती है: इनपुट का हर चर 0 और 255 के बीच का कोड पॉइंट रखता हो। फ़ंक्शन कोड पॉइंट पढ़ता है, UTF-8 बाइट्स नहीं, इसलिए "é" (कोड पॉइंट 233) आराम से पार हो जाता है, जबकि "你" (कोड पॉइंट 20320) एक भी चर एन्कोड होने से पहले ही DOMException नामक InvalidCharacterError थ्रो कर देता है। सीमा "ASCII" नहीं है, "Unicode" नहीं है, बिल्कुल 256 है, और इसमें नीचे वाले कंट्रोल चर भी शामिल हैं - NUL बाइट का एन्कोडिंग क़ानूनी और मायने वाली चीज़ है, और यही इस फ़ंक्शन के अस्तित्व के कारणों में से एक है।

पूरा व्यवहार, पंक्ति-दर-पंक्ति:

इनपुट रिज़ल्ट
"Hello, World!" "SGVsbG8sIFdvcmxkIQ==" - पाठ्यपुस्तक का मामला
"" (ख़ाली स्ट्रिंग) "" - ख़ाली इनपुट, ख़ाली आउटपुट
"\u0000" (NUL) "AA==" - कंट्रोल चर पहली कतार के नागरिक हैं
"a\u00e9z" (é, कोड पॉइंट 233) "Yel6" - पूरा Latin1 रेंज पार कर जाता है
"\u0100" (कोड पॉइंट 256) InvalidCharacterError थ्रो होता है - सीमा से एक कदम आगे
"h\u4f60" (你, कोड पॉइंट 20320) InvalidCharacterError थ्रो होता है - हर इमोजी भी, क्योंकि वे सब 255 से बहुत ऊपर हैं

दो अमली नोट्स। एरर मैसेज इंजन से इंजन अलग होता है - Firefox कहता है "String contains an invalid character", Chrome कहता है कि स्ट्रिंग में "contains characters outside of the Latin1 range" है - इसलिए किसी भी डिफेंसिव कोड में एक्सेप्शन के नाम पर कैच करें। और थ्रो पहले ही दोषी चर पर होता है, अंत में नहीं: btoa स्ट्रिंग का आधा हिस्सा एन्कोड करके माफ़ी माँगता नहीं है। जब आप जानबूझकर Latin1 व्यवहार चाहते हैं (ऐसी बाइट स्ट्रिंग का एन्कोडिंग जो 0-255 कोड पॉइंट्स से जानबूझकर बनी हो), तो फ़ंक्शन बिल्कुल वही कर रहा है जिसकी आपने माँगी, और उपर की टेबल इसका पूरा व्यक्तित्व है।

बाइट्स का पुल

तो सवाल बन जाता है: असली डेटा - आपके टेक्स्ट के UTF-8 बाइट्स, फ़ाइल की सामग्री, कैनवास का आउटपुट - btoa() के इनपुट में कैसे पहुँचता है? जवाब है "बाइट्स पुल": एक JavaScript स्ट्रिंग जिसमें हर चर एक बाइट वैल्यू रखता है, वही चाल जो डिकोडर बनाते हैं और जो btoa नेटिव रूप से समझता है। नाइव वर्ज़न एक लूप है:

function bytesToBase64 (bytes) {
  let binary = '';
  for (let i = 0; i < bytes.length; i += 1) {
    binary += String.fromCharCode(bytes[i]);
  }
  return btoa(binary);
}

सही है, पर लूप में स्ट्रिंग जोड़ना बड़ी फ़ाइलों के लिए धीमा है, और मशहूर छोटकरी - String.fromCharCode.apply(null, bytes), जो पूरा ऐरे एक ही कॉल में आर्गुमेंट्स के तौर पर फ़ीड करती है - एक सख़्त कलीफ़ रखती है। फ़ंक्शन कॉल में आर्गुमेंट्स की संख्या की सीमा होती है, और वह आपकी पहली मेगाबाइट से बहुत पहले आ जाती है:

const big = new Uint8Array(1000000);
btoa(String.fromCharCode.apply(null, big));
// RangeError in Firefox: "too many arguments provided for a function call"
// RangeError in Chrome: "Maximum call stack size exceeded"

वह इलाज जिसने किसी दूसरे एकल बदलाव से ज़्यादा फ़ाइल-अपलोड फ़ीचर बचाए हैं, है पुल को चंक पार करना - एक-एक बार में कुछ हज़ार चर - और रिज़ल्ट्स को जोड़ना:

function bytesToBase64Chunked (bytes) {
  const CHUNK = 0x8000;
  const parts = [];
  for (let i = 0; i < bytes.length; i += CHUNK) {
    parts.push(String.fromCharCode.apply(null, bytes.subarray(i, i + CHUNK)));
  }
  return btoa(parts.join(''));
}

हर चंक इतना छोटा है कि apply करना सुरक्षित है, subarray बिना कॉपी के एक व्यू देता है, और join वही बिल्कुल बाइनरी स्ट्रिंग बनाता है जो लूप बनाता। अब सिक्के का टेक्स्ट वाला पक्ष। किसी भी असली टेक्स्ट के लिए, TextEncoder - प्लेटफ़ॉर्म का UTF-8 एन्कोडर, Firefox 18, Chrome 38, Safari 10.1 और उसके बाद हर जगह उपलब्ध - पुल के काम करने से पहले आपकी स्ट्रिंग को साफ़-सुथरे बाइट्स में बदल देता है:

const bytes = new TextEncoder().encode('hello 你好');
const base64 = bytesToBase64Chunked(bytes);
console.log(base64); // "aGVsbG8g5L2g5aW9"

वह आउटपुट ही वायर पर "hello 你好" का असली रूप है: छह ASCII बाइट्स प्लस दो चीनी अक्षरों के लिए छह UTF-8 बाइट्स, सब एक ही प्रिंटेबल निकाल में। अगर आपका टेक्स्ट UTF-8 नहीं है - और वेब पर, वह आमतौर पर होता है - तो आपको पहले दूसरा कैरेक्टर सेट चाहिए, यानी उसे ऐसी जगह एन्कोड करवाना जहाँ वह कैरेक्टर सेट बोला जाए, आमतौर पर सर्वर। TextEncoder जानबूझकर अंदाज़ा लगाने से इनकार करता है, और वह सही कहता है।

2025 की छोटकरी: Uint8Array.toBase64

अगर आपके पास पहले से एक Uint8Array है, तो पुल एक दुगली है, क्योंकि नई ECMAScript (ES2026) सुविधा ऐरे को सीधे एन्कोड करती है: bytes.toBase64(options)। यह Chrome 140, Edge 140, Firefox 133, Safari 18.2, Node 25 और Deno 2.5 में उतरी है - उसके डिकोडिंग-भाई वाली उसी Baseline 2025 लहर में - और यह दो ऑप्शन लेता है जो इसे प्लेटफ़ॉर्म का सबसे बहुमुखी एन्कोडर बना देते हैं। पहला alphabet: "base64" (डिफ़ॉल्ट) या "base64url"। दूसरा omitPadding: इसे true पर सेट करें और अंत वाले = चर उतर जाते हैं, जो सबसे ज़्यादा URL-अनुकूल उपभोक्ताओं की पसंदीदा शक्ल है। ऑप्शन के तौर पर कुछ और पास करने पर TypeError थ्रो होता है, यानी API आपके टाइपो पर विनम्रता से आपत्ति जता रहा है:

const bytes = new Uint8Array([251, 255]);
console.log(bytes.toBase64()); // "+/8="
console.log(bytes.toBase64({ omitPadding: true })); // "+/8"
console.log(bytes.toBase64({ alphabet: 'base64url' })); // "-_8="

वह दो बाइट्स वर्णमाला के लिए जितना हो सके सबसे तिरस्कारी चुने गए हैं: स्टैंडर्ड मोड में वे + और / देते हैं, इसलिए आख़िरी पंक्ति ठीक वही दिखाती है जो base64url पर स्विच करते समय बदलता है। परफ़ॉर्मेंस चुपचाप बोनस है: एक ताज़ा Firefox पर, toBase64 से दस मेगाबाइट एन्कोड करने में करीब पाँच मिलीसेकंड लगते हैं, जबकि उपर दिया स्ट्रिंग-पुल वाला रास्ता करीब पंद्रह गुना ज़्यादा लेता है, क्योंकि वह रास्ते में एक विशाल इंटरमीडिएट स्ट्रिंग बनाता है। पुराने ब्राउज़रों पर कुछ मेगाबाइट तक का काम पुल बिल्कुल उपयोगी रहता है - और पिछले सेक्शन की वजहों के लिए यही वह चंकड वर्ज़न है जो आपको चाहिए।

URL-सुरक्षित आउटपुट

Base64 के पास उन जगहों के लिए एक ख़ास वैरिएंट है जहाँ +, / और = ख़राबी मचाते हैं, और यह अपना अलग सेक्शन पाता है, क्योंकि इतना ज़्यादा ख़राब कोड बस वही स्टैंडर्ड Base64 है जो URL से टकरा गया। क्वेरी स्ट्रिंग में + स्पेस है; पाथ में / सेपरेटर; और = को कुछ पोज़ीशन में परसेंट-एन्कोडिंग चाहिए। RFC 4648, सेक्शन 5 की URL- और फ़ाइल-नाम-सुरक्षित वर्णमाला - base64url - उन दो चरों की जगह - और _ रखती है, और चूँकि रिसीविंग साइड पर डेटा की लंबाई आमतौर पर मालूम होती है, वह पैडिंग को बिल्कुल उतारने की भी इज़ाज़त देती है। आउटपुट URLSearchParams, पाथ सेगमेंट्स, फ़्रैगमेंट्स और फ़ाइल-नामों से बिने एक परसेंट निशान के गुज़रता है।

2025 की API के साथ यह बस एक ऑप्शन ऑब्जेक्ट है:

const params = new URLSearchParams();
params.set('payload', bytes.toBase64({ alphabet: 'base64url', omitPadding: true }));
console.log(params.toString()); // "payload=-_8" - कोई परसेंट-एन्कोडिंग नहीं

पुराने ब्राउज़रों पर, btoa से एन्कोड करके बदलाव करें। दो replace और एक trim से पूरा काम हो जाता है:

function toUrlBase64 (base64) {
  return base64
    .replace(/\+/g, '-')
    .replace(/\//g, '_')
    .replace(/=+$/, '');
}
console.log(toUrlBase64(btoa('hi?/x'))); // "aGk_L3g"

तीन नियम चैनल को साफ़ रखते हैं। हर चैनल के लिए एक वर्णमाला चुनें और उसी पर टिके रहें - + और - मिलाकर वाली वैल्यू न किसी परिवार की है, और कोई डिकोडर यह भी अंदाज़ा नहीं लगा पाएगा कि आपका मतलब किससे था। पैडिंग एक करार है, सलाह नहीं: अगर आप उसे उतारें, तो रिसीवर बिना-पैड वैल्यू के लिए तैयार होना चाहिए, और अगर आप रखें, तो रिसीवर को उससे घुटन न होनी चाहिए (ब्राउज़र ढीले हैं, कुछ JSON स्कीमा नहीं हैं)। और याद रखें कि बदलाव उलटने लायक और बिना हानि वाला है - - और _ वही 62वीं और 63वीं वर्णमाला-पोज़ीशन लेते हैं जो + और / पहनते हैं, इसलिए दोस्ताना जोड़ी चुनने से कुछ नहीं खोता।

छवियों को सफ़र पर भेजना: डेटा URLs

ब्राउज़र में Base64 का सबसे पुराना और सबसे नज़र आने वाला इस्तेमाल डेटा URL है: data:, वैकल्पिक मीडिया टाइप, वैकल्पिक ;base64 फ़्लैग, एक कॉमा, और फिर पेलोड। टेक्स्ट पेलोड्स परसेंट-एन्कोडेड होते हैं; बाइनरी पेलोड्स - छवियाँ, फ़ॉन्ट, ऑडियो - Base64 होते हैं, और ब्राउज़र उन्हें शून्य HTTP रिक्वेस्ट के साथ रेंडर करता है। किसी ऐसी छवि फ़ाइल के लिए जो यूज़र अभी चुकी है, FileReader एन्कोडिंग आपकी जगह कर देता है और तैयार URL लौटा देता है:

const reader = new FileReader();
reader.onload = () => {
  console.log(reader.result); // "data:image/png;base64,iVBORw0KGgo..."
  imageElement.src = reader.result;
};
reader.readAsDataURL(file);

रिज़ल्ट एक बने-बना हुआ src है, एक वैल्यू जो आप localStorage में स्टोर कर सकते हैं या JSON बॉडी में भेज सकते हैं। अगर छवि कैनवास पर है - स्क्रीनशॉट, प्रोसेस फ़ोटो, जनरेट चार्ट - तो canvas.toDataURL() सबसे पुराने ब्राउज़र रिलीज़ से यह काम कर रहा है, और यह फ़ॉर्मेट चुनने देता है और, लॉसी फ़ॉर्मेट्स के लिए, क्वालिटी भी:

const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
ctx.drawImage(photo, 0, 0);
const pngUrl = canvas.toDataURL('image/png');
const jpegUrl = canvas.toDataURL('image/jpeg', 0.8);

तीन गड्ढे हैं जिनके इर्द-गिर्द योजना बनानी है। पहला, टेंटेड कैनवास का नियम: अगर आपने CORS अनुमति के बिना क्रॉस-ओरिजिन छवि कैनवास पर खींची, तो पिक्सल वापस पढ़ने की हर कोशिश - toDataURL समेत - SecurityError थ्रो करती है। इलाज है छवि को crossOrigin = 'anonymous' के साथ लोड करना और पक्का करना कि सर्वर सही हेडर्स भेजता है। दूसरा, क्वालिटी आर्गुमेंट PNG के लिए नज़रअंदाज़ होता है और सिर्फ़ JPEG (और WebP) के लिए मायने रखता है - "मेरा PNG बड़ा क्यों है" का आम स्रोत। तीसरा, और सबसे बड़ा: पेलोड फ़ाइल से करीब 33 फ़ीसदी बड़ा है, और वह पेज में स्ट्रिंग के रूप में बैठता है। ऐसी छवियों के लिए जो कभी ब्राउज़र से बाहर नहीं जातीं, एक मुफ़्त विकल्प है - ऑब्जेक्ट URL, जो Blob को बिल्कुल एन्कोड किए बिना लपेट लेता है:

const objectUrl = URL.createObjectURL(blob);
imageElement.src = objectUrl;
URL.revokeObjectURL(objectUrl); // जब काम ख़त्म हो जाए

इससे निकलने वाली काम की बाँट: पेज पर रहने वाली हर चीज़ के लिए ऑब्जेक्ट URL, और जो कुछ कॉपी होना चाहिए, स्टोर होना चाहिए, या टेक्स्ट के रूप में भेजना चाहिए, उसके लिए डेटा URL। दोनों पहली कतार के हैं; वे बस अलग-अलग मुद्दों को हल कर रहे हैं।

JWT बनाना और साइन करना

अगर आप ब्राउज़र में टोकन जनरेट करते हैं - सेल्फ-होस्टेड ऑथेंटिकेशन फ़्लो, डेमो, या सर्वरलेस फ्रंट एंड के लिए - तो कम्पैक्ट JWS फ़ॉर्मेट तीन base64url सेगमेंट है: हेडर, पेलोड, सिग्नेचर, कहीं भी बिना पैडिंग। साइन करने का काम Web Crypto API संभालती है; एन्कोडिंग ठीक उसी URL-सुरक्षित आउटपुट की है जो दो सेक्शन पहले था:

const encoder = new TextEncoder();
const segment = (bytes) =>
  bytes.toBase64({ alphabet: 'base64url', omitPadding: true });
const header = segment(encoder.encode(JSON.stringify({ alg: 'HS256', typ: 'JWT' })));
const payload = segment(encoder.encode(JSON.stringify({ sub: '1234567890', name: 'John Doe' })));
const key = await crypto.subtle.importKey(
  'raw',
  encoder.encode('shared-secret'),
  { name: 'HMAC', hash: 'SHA-256' },
  false,
  ['sign']
);
const signature = segment(
  new Uint8Array(
    await crypto.subtle.sign('HMAC', key, encoder.encode(header + '.' + payload))
  )
);
const token = header + '.' + payload + '.' + signature;

प्लंबिंग से ज़्यादा दो विवरण मायने रखते हैं। सिग्नेचर ठीक header + '.' + payload को कवर करती है - राव सेगमेंट्स, JSON नहीं - इसलिए किसी भी हिस्से में कोई भी बदलाव टोकन को निरर्थन बना देता है, यानी पूरा मक़्सद। और crypto.subtle.sign राव ArrayBuffer लौटाती है, इसीलिए सेगमेंट एन्कोडर से पहले उसे एक पंक्ति में Uint8Array में लपेटा गया है। RSA-आधारित टोकनों के लिए फ़्लो RS256 और की पेयर के साथ एक जैसा है, और अगर आप पब्लिक कुंजी को JWK के रूप में एक्सपोर्ट करें (crypto.subtle.exportKey('jwk', key)), तो न्यूमरिक मम्बर्स - n, e, और प्राइवेट कुंजियों के लिए d, p, q - अपने आप बिना-पैड base64url के रूप में निकलते हैं। सिक्योरिटी की चेतावनियाँ किसी भी टोकन जैसी ही हैं: alg: "none" वाला हेडर सत्यापन छोड़ने का आग्रह है, टाइम क्लेम्स (exp, nbf) लागू किए जाने चाहिए, और ऐसा सर्वर जो उसी ऑडियंस के लिए HMAC और RSA दोनों स्वीकार करे, क्लासिक की-कन्फ़्यूज़न का दरवाज़ा खोलता है। सही एन्कोड करें, सही साइन करें, और रिसीविंग साइड पर सत्यापित करें।

ऑथेंटिकेशन हेडर्स

वेब की सबसे साधारण ऑथेंटिकेशन स्कीम Base64 क्या है और क्या नहीं, इस पर भी सबसे शिक्षाप्रद है। HTTP Basic Authorization: Basic भेजती है, और उसके बाद username:password का Base64 - एक कॉल, बाइट्स पुल की ज़रूरत नहीं, क्योंकि यूज़रनेम और पासवर्ड (उम्मीद है) प्लेन टेक्स्ट हैं:

const credentials = btoa('alice:secret123');
fetch('/api/me', {
  headers: { Authorization: 'Basic ' + credentials }
});
// Authorization: Basic YWxpY2U6c2VjcmV0MTIz

और यही वह सबक है जो एक पंक्ति में समा जाता है: Base64 एन्क्रिप्शन नहीं है। उपर दिया हेडर alice:secret123 से एक atob कॉल दूर है - अटैकर के लिए और लॉग पढ़ने वाले हर किसी के लिए - इसलिए Basic ऑथेंटिकेशन सिर्फ़ HTTPS पर स्वीकार्य है, जहाँ ट्रांसपोर्ट ही असली सुरक्षा है और Base64 बस फ़ॉर्मेटिंग। एक रिक्वेस्ट से ज़्यादा उम्र वाली हर चीज़ के लिए टोकन-आधारित स्कीम ही बेहतर: Bearer टोकन भी एक ही हेडर है, पर वह एक रैंडम वैल्यू है जिसका सीक्रेट कभी हेडर में ले जाने की ज़रूरत ही नहीं, और उसे रिवोक किया जा सकता है। दोनों के बीच एन्कोडिंग का चुनाव तिरस्करनीय है - दोनों btoa या प्लेन टेक्स्ट हैं - पर सिक्योरिटी का चुनाव तिरस्करनीय नहीं, और वह इरादे से लिया जाना चाहिए।

फ़ाइल अंदर, टेक्स्ट बाहर

अपलोड वही जगह है जहाँ 33 फ़ीसदी का कर असली पैसे में जुड़ता है, क्योंकि फ़ाइल आमतौर पर पेज का सबसे बड़ा हिस्सा होती है। दो रास्ते हैं, और पहला वही है जो आपको डिफ़ॉल्ट रूप से लेना चाहिए: multipart फ़ॉर्म डेटा। FormData फ़ाइल को स्टैंडर्ड बॉडी में राव बाइट्स के रूप में साथ लेती है, फ्रेमिंग ब्राउज़र करता है, और इस पूरी तसवीर में Base64 कहीं नहीं है - न साइज़ का कर, न इंटरमीडिएट स्ट्रिंग, और बाइट्स जैसे-जैसे पढ़े जाते हैं वैसा ही सर्वर की ओर स्ट्रीम होते हैं:

const form = new FormData();
form.append('upload', file);
await fetch('/api/upload', { method: 'POST', body: form });

दूसरा रास्ता उन API के लिए है जो फ़ाइल को स्ट्रिंग के रूप में JSON बॉडी में आना माँगते हैं - कुछ सर्वरलेस फ़ंक्शन, कुछ मोबाइल बैकएंड, कुछ लीगेसी सर्विसेज़। वहाँ एन्कोडिंग हर फ़ाइल के लिए एक पंक्ति की है, और ख़र्च ठीक वही है जो कर कहता है: 5 मेगाबाइट की फ़ाइल 6.7 मेगाबाइट की स्ट्रिंग बनती है, जो फिर JSON में सिरीलाइज़ होती है, जो फिर भेजी जाती है। फ़ोटो के लिए ठीक, वीडियो के लिए दर्दनाक:

const bytes = new Uint8Array(await file.arrayBuffer());
const body = JSON.stringify({
  name: file.name,
  content: bytes.toBase64()
});
await fetch('/api/upload-json', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body
});

उस रास्ते पर बड़ी फ़ाइलों के लिए, एक विशाल स्ट्रिंग एक ही कॉल में मत बनाएँ - स्लाइसों में बनाएँ, जहाँ हर स्लाइस तीन बाइट्स का गुणज हो। वही एलाइनमेंट है जो चाल को क़ानूनी बनाता है: तीन बाइट्स का गुणज बिना पैडिंग के चार चरों के साफ़ गुणज में एन्कोड होता है, इसलिए अलग-अलग एन्कोड की गई स्लाइसों का जोड़ पूरी फ़ाइल की एन्कोडिंग से बिल्कुल मेल खाता है, और सिर्फ़ आख़िरी स्लाइस ही पैडिंग लेती है:

async function encodeLargeFile (file) {
  const bytes = new Uint8Array(await file.arrayBuffer());
  const SLICE = 3 * 1000 * 1000;
  const parts = [];
  for (let i = 0; i < bytes.length; i += SLICE) {
    parts.push(bytes.subarray(i, i + SLICE).toBase64());
  }
  return parts.join('');
}

वही एलाइनमेंट वाली सोच है जिसके लिए आप कभी Base64 स्ट्रिंग को किसी मनमानी जगह से तोड़कर टुकड़ों को अपने आप डिकोड होने की उम्मीद नहीं रखने चाहिए - तीन-बाइट ग्रुप ही परमाणु है, और उसके बीच की कट एक लटकता फ़्रैगमेंट छोड़ती है। डाउनलोड इसका आइना है: किसी जनरेट की गई फ़ाइल के लिए, छोटी हो तो वह डाउनलोड लिंक पर डेटा URL से बाहर जा सकती है, पर ज़्यादा मायने वाली किसी भी चीज़ के लिए Blob प्लस ऑब्जेक्ट URL ही सही रास्ता है, क्योंकि ब्राउज़र को शुरू से ही पूरा पेलोड स्ट्रिंग के रूप में साथ लेना नहीं पड़ता।

स्टेट स्टोर करना और शेयर करना

दो और टेक्स्ट-ऑनली चैनल जहाँ Base64 असली काम करता है। पहला स्टोरेज है: localStorage और sessionStorage स्ट्रिंग्स रखते हैं, इसलिए स्ट्रक्चर्ड या बाइनरी डेटा अंदर जाने से पहले एन्कोड हो जाता है। रउंड ट्रिप एक एन्कोड और एक डिकोड है, और दोनों पक्ष एक साथ देखने लायक, क्योंकि स्टोरेज बग लगभग हमेशा दोनों के बीच का कैरेक्टर-सेट अंतर होता है:

const state = { theme: 'dark', draft: 'hello' };
const packed = new TextEncoder().encode(JSON.stringify(state));
localStorage.setItem('app-state', new Uint8Array(packed).toBase64());
const raw = atob(localStorage.getItem('app-state'));
const bytes = Uint8Array.from(raw, (c) => c.codePointAt(0));
const state = JSON.parse(new TextDecoder().decode(bytes));

बजट पर ग़ौर रखें, हालाँकि: हर ओरिजिन को लगभग 5 मेगाबाइट localStorage मिलते हैं, आपकी स्टोर की गई स्ट्रिंग डेटा से 33 फ़ीसदी मोटी है, और पेज खुले रहते वह स्ट्रिंग मेमोरी में UTF-16 के रूप में भी रहती है - दोबारा उसकी लंबाई का दोगुना। 3 मेगाबाइट का एसेट 4 मेगाबाइट स्टोरेज और 8 मेगाबाइट मेमोरी है, यही वह तरीका है जिससे "छोटा" फ़ीचर क्वोटा एरर बन जाता है। दूसरा चैनल URL ख़ुद है: शेयर लिंक, डीप लिंक, और OAuth स्टेट - सब स्ट्रक्चर्ड डेटा को किसी ऐसी जगह चाहते हैं जो कॉपी-पेस्ट को पार कर सके। रेसिपी है कम्पैक्ट स्टेट, JSON, फिर बिना पैडिंग base64url, ताकि वैल्यू को परसेंट-एन्कोडिंग की बिल्कुल ज़रूरत न पड़े - और पूरा URL कुछ हज़ार चरों से छोटा रखें, वहीं से पुराने क्लाइंट्स, प्रॉक्सी, और लॉगिंग टूल्स घबरा जाने लगते हैं।

ईमेल और MIME

Base64, वेब से पुराना है, और उसकी गृहभूमि ईमेल है। Content-Transfer-Encoding: base64 वाले MIME अटैचमेंट वही तरीका हैं जिससे बाइनरी फ़ाइल टेक्स्ट प्रोटोकॉल के अंदर सवारी करती है, और मैसेज फ़ॉर्मेट की पुरानी 76-चर लाइन सीमा से निकला वह रवाज़ जानने लायक है: एन्कोडेड बॉडी को 76 चरों पर लाइन रैप करें। ब्राउज़र SMTP भेज नहीं सकता, पर वह दो ईमेल-से-जुड़े काम करता है - MIME बॉडी बनाना जिसे बैकएंड रीले भेजेगा, और प्राप्त संदेशों के अटैचमेंट दिखाना - और दोनों एन्कोडिंग को छूते हैं। रैपिंग ख़ुद एक दो-पंक्ति फ़ंक्शन है, और क्रम मायने रखता है: पहले एन्कोड, दूसरा रैप, क्योंकि btoa इनपुट में नई लाइन पर थ्रो नहीं करता - वह ब्रेक को पेलोड बाइट के रूप में एन्कोड कर देता है, और आपकी लाइन ब्रेक्स आउटपुट के अंदर ख़त्म हो जाती हैं:

function wrapForMime (base64, width) {
  const w = width || 76;
  return base64.match(new RegExp('.{1,' + w + '}', 'g')).join('\r\n');
}

रिसीविंग साइड वह मुफ़्त वाला है: atob अपनी स्टैंडर्ड व्यवहार का हिस्सा बनाकर ASCII व्हाइटस्पेस छला देता है, इसलिए रैप MIME बॉडी बिल्कुल वैसा ही डिकोड होता है जैसे आई थी, नई लाइनें समेत, कोई अन-रैप स्टेप बिना। अगर आप वेबमेल क्लाइंट या अटैचमेंट पिकर बना रहे हैं, तो वह एक असममितता - एन्कोडर को साफ़ लाइनें निकालनी ही हैं, डिकोडर को फ़र्क़ नहीं पड़ता - पूरी MIME कहानी है, एक वाक्य में।

कब लाइब्रेरी उठाएँ

उपर दिए नेटिव औज़ारों के साथ, लाइब्रेरी की ज़रूरत दुर्लभ है, और ईमानदार सलाह है: डिफ़ॉल्ट रूप से प्लेटफ़ॉर्म पर रहें, और पैकेज तभी जोड़ें जब कोई असली ज़रूरत उसे इशारा करे। तीनों, जो कोडबेस में वाक़ई सामने आते हैं:

js-base64 (npm install js-base64) वह जेनरल-प्युरपोज़ वाला है: एक छोटा शुद्ध JavaScript ट्रांसकोडर जो UTF-8 स्ट्रिंग्स को पहली कतार के नागरिक मानता है - CJK स्ट्रिंग पर Base64.encode UTF-8 नृत्य आपकी जगह कर देता है - और, डिकोडिंग के लिए एन्कोडिंग जितना ही फ़ायदेमंद, decode में दोनों वर्णमालाएँ स्वीकार करता है और isValid जाँच भी शिप करता है। यह सही जवाब है जब आप ऐसे ब्राउज़रों को टारगेट कर रहे हों जहाँ 2025 की API ग़ायब हैं और आप एक इम्पोर्ट से स्ट्रिंग्स और बाइट्स दोनों कवर करना चाहते हैं:

import { Base64 } from 'js-base64';
const encoded = Base64.encode('小飼弾'); // "5bCP6aO85by+" - UTF-8 आपकी जगह संभाल लिया
const decoded = Base64.decode('5bCP6aO85by-'); // स्टैंडर्ड और URL-सुरक्षित दोनों पढ़ता है
const valid = Base64.isValid(encoded); // true

base64-js वह बाइट-फ़ोकस्ड वाला है: Uint8Array पर fromByteArray और toByteArray, कोई डिपेंडेंसी नहीं, पुराने browserify इकोसिस्टम का कामगार, और आज भी एक बढ़िया चुनाव, जब आपका कोड टाइप्ड ऐरे में रहता है और आप चाहते हैं कि एन्कोडिंग बाइट्स की शुद्ध फ़ंक्शन हो। और अगर आपकी लाइब्रेरी चाहने की वजह "मैं 2025 की API पसंद करता हूँ पर 2025 के ब्राउज़र माँग नहीं सकता" है, तो जवाब किसी Base64 पैकेज का नहीं, पॉलीफ़िल का है: core-js (और वह Babel प्रीसेट जो उसे खींच लाता है) Uint8Array.fromBase64 और साथियों को इम्प्लीमेंट करता है, ताकि आप नए स्टाइल का कोड एक बार लिख दें और शिम को पुराने इंजन पर खाली जगह भरने दें। बाधा के हिसाब से चुनें - पुराने ब्राउज़र, स्ट्रिंग सुविधा, या बाइट शुद्धता - आदत से नहीं।

वे गड्ढे जिनमें डेवलपर्स घंटों डूब जाते हैं

  • ऐसी स्ट्रिंग पर btoa कॉल करना जिसमें कोड पॉइंट 255 से ऊपर का चर हो। यह थ्रो करता है, गड़बड़ नहीं करता, और पहले ही दोषी पर रुक जाता है। इलाज हमेशा वही है: पहले TextEncoder, दूसरा पुल।
  • बड़े ऐरे पर fromCharCode.apply कलीफ़। एक मिलियन आर्गुमेंट्स दोनों बड़े इंजन में RangeError हैं। पुल को चंक करें, या toBase64 पर चले जाएँ।
  • वह साइज़ का कर जिससे सबसे ज़्यादा चोट लगती है, भूलना: स्टोरेज। localStorage में फ़ाइल फ़ाइल से 33 फ़ीसदी बड़ी है, और क्वोटा हर ओरिजिन के लिए है, आपके ऐप की बाक़ी सब स्टोरेज के साथ साझा।
  • स्टैंडर्ड Base64 का क्वेरी स्ट्रिंग से टकराना। + स्पेस बनकर पहुँचता है, / पाथ तोड़ देता है, और बग रिपोर्ट कहती हैं "API अस्थिर है"। URL-सुरक्षित आउटपुट, बिना पैडिंग, और बग का पूरा ज़नर गायब।
  • सर्विसेज़ के बीच असंगत पैडिंग। एक गेटवे = रखता है, दूसरा उतार देता है, तीसरा वापस जोड़ देता है। रिसीवर दोनों शक्लों के लिए तैयार होना चाहिए, और करार को यह बताना चाहिए कि कौन सी कैनोनेकल है।
  • Base64 को ताला मान लेना। यह सिरीलाइज़ेशन फ़ॉर्मैट है, प्लेन टेक्स्ट से एक फ़ंक्शन कॉल दूर, और सिक्योरिटी रिव्यू में "Base64 से एन्कोड किया गया" एक फ़ाइंडिंग है, कोई कंट्रोल नहीं।
  • बाइनरी स्ट्रिंग्स को मेमोरी मॉडल मानना। एक डिकोड या एन्कोड मेगाबाइट UTF-16 में दो मेगाबाइट सवार है; Uint8Array एक में पकड़ता है। बड़े पेलोड के लिए बाइट्स को शुरू से अंत तक टाइप्ड ऐरे में रखें।
  • डबल एन्कोडिंग। जो वैल्यू पहले से Base64 था, उसे फिर एन्कोड किया जाता है, और उपभोक्ता एक बार डिकोड करके डेटा की जगह अक्षरों की स्ट्रिंग पाने लगता है। शक हो तो लपेटने से पहले जाँचें - जो स्ट्रिंग पहले से वर्णमाला में है और वैध पैडिंग के साथ, वह गंध है।
  • JWT पेलोड पर यही भरोसा कि वह साफ़ डिकोड हुआ। डिकोड होना, असलियत नहीं है। एक भी क्लेम पढ़ने से पहले सही कुंजी और सही एल्गोरिदम से सिग्नेचर सत्यापित करें।

परफ़ॉर्मेंस: एक मिलियन बाइट्स का ख़र्च

ब्राउज़र में Base64, जहाँ कभी महंगा था, वहाँ अब सस्ता है, और बजट में अब एक की जगह तीन पंक्तियाँ हैं। CPU: एक ताज़ा Firefox पर, Uint8Array.toBase64 दस मेगाबाइट को करीब पाँच मिलीसेकंड में एन्कोड कर देता है, जबकि चंकड btoa पुल करीब पंद्रह गुना ज़्यादा लेता है - btoa धीमा होने की वजह से नहीं, बल्कि पुल के रास्ते में विशाल इंटरमीडिएट स्ट्रिंग बनाने की। अगर आपका एन्कोडिंग बजट मिलीसेकंड में है, तो नेटिव मीथड इस्तेमाल करें; अगर आप 2 किलोबाइट का कॉन्फ़िग ऑब्जेक्ट एन्कोड कर रहे हैं, तो दोनों समझ के सीमा से नीचे हैं। बैंडविड्थ: यह स्थायी कर है - हर बाइट जो आप एन्कोड करते हैं, वायर पर 1.33 बाइट का ख़र्च आता है, प्लस ट्रांसपोर्ट का जो भी फ्रेमिंग जोड़ता है। एन्कोडिंग को "ऑप्टिमाइज़" करने से पहले ट्रांसफ़र नाप लें। मेमोरी: एन्कोडेड स्ट्रिंग वह सबसे बड़ा ट्रेंज़िएट एलोकेशन है जो आप बनाएँगे, और 5 मेगाबाइट की फ़ाइल के लिए यह 6.7 मेगाबाइट की स्ट्रिंग है, या पेज उसे पकड़े रहते करीब 13.4 मेगाबाइट UTF-16 मेमोरी। अमली परिणाम गणित से निकलते हैं: बड़े एन्कोडिंग को स्लाइस करें ताकि कोई एक स्ट्रिंग विशाल न हो, स्ट्रिंग बनते ही इंटरमीडिएट बाइट्स छोड़ दें, बाइट्स को प्रिंटेबल होने की ज़रूरत ही न हो तो ऑब्जेक्ट URL और multipart को पसंद करें, और अगर मेन थ्रेड को सहज स्क्रॉलिंग बनाए रखनी है तो मल्टी-मेगाबाइट काम को Web Worker पर सौंप दें। फ़ॉर्मेट करीब चार दशकों पुराना है; प्लेटफ़ॉर्म आख़िरकार उसके कदमों में आ गया।

ब्राउज़रों ने एन्कोडिंग कैसे सीखी

एन्कोडर का अपना इतिहास है, और वह आपको मिलने वाले अवशेषों की व्याख्या करता है। btoa - "बाइनरी से ASCII", नाम का अर्थ अक्षरशः वही है, और atob बस वही शब्द उल्टे हैं - 2011 में HTML स्पेसिफ़िकेशन में लिखा गया, उन ब्राउज़रों से रीवर्स-इंजीनियर किया गया जिनमें वह पहले से शिप हो चुका था: Firefox 2004 से, Safari 3, Chrome 4। Internet Explorer, अपनी बनी-बाने आदत के तौर पर, दोनों फ़ंक्शन 2012 के वर्ज़न 10 तक छोड़ता रहा, और वही एक अनुपस्थिति वजह है कि एक दशक का JavaScript हाथ से बनी Base64 टेबल से भरपूर है, और Unicode के लिए एक ख़ास मंत्र: btoa(unescape(encodeURIComponent(str)))। वह काम करता था - encodeURIComponent परसेंट-एस्केप्ड UTF-8 बनाता है, और unescape उसे बाइट स्ट्रिंग में बदल देता था - पर वह unescape() पर बना था, जोड़ी का वही जिसे लैंग्वेज ने डीप्रेकेट कर दिया था, और यह बस आदत के बल पर ब्राउज़र कोड में सालों जीता। न्यायपूर्ण इलाज Encoding मानक के साथ आया: TextEncoder और TextDecoder, Firefox 18 (2013), Chrome 38 (2014), Safari 10.1 (2017) में, और किसी भी IE वर्ज़न में कभी नहीं - एक और IE खाई, एक और दशक की वर्कअराउंड। Node.js कहानी का सर्वर-साइड आधा हिस्सा बताता है: उसके पास पहले दिन से Buffer प्लस Base64 था, पर atob और btoa ग्लोबल के रूप में सिर्फ़ 2021 के वर्ज़न 16 से, उससे पहले दो छोटे npm शिम उस बोझ को उठा रहे थे। और फिर, 2024 के अंत और 2025 के दौरान, लैंग्वेज ने ख़ुद Base64 शिप किया - Uint8Array.toBase64 और साथी Firefox 133 (नवंबर 2024), Safari 18.2 (दिसंबर 2024), Chrome 140 (सितंबर 2025) और Node 25 (अक्टूबर 2025) में - और सुविधा Baseline 2025 चिह्नित हुई - वही सेट जो प्लेटफ़ॉर्म बीस साल से हेल्परों से अंदाज़न की तरह कर रहा था, अब स्टैंडर्ड। लेख के आख़िरी मज़ेदार तथ्य ज़्यादातर इसी बारे में हैं कि हर टुकड़े के आने में कितना समय लगा।

क्या आप जानते हैं?

  • फ़ंक्शन नाम एक वाक्य हैं: btoa "बाइनरी से ASCII" है और atob "ASCII से बाइनरी"। दिशा नाम में ही है, यही वजह है कि यह जोड़ा 2000 के दशक से ख़ुद-दस्तावेज़ है।
  • कंप्यूटिंग के इतिहास में सबसे ज़्यादा एन्कोड की गई स्ट्रिंग शायद "hello" है: btoa('hello') aGVsbG8= है, पृथ्वी के हर ट्यूटोरियल, हर टेस्ट सूट, और हर इंटरव्यू वाइटबोर्ड का आउटपुट।
  • हर वैध Base64 स्ट्रिंग की लंबाई चार का गुणज होती है, पैडिंग समेत। = चर एक फिंगरप्रिंट हैं: एक का मतलब आख़िरी ग्रुप में दो बाइट्स थे, दो का मतलब एक।
  • MIME और अधिकांश कमांड-लाइन टूल्स में 76-चर की लाइन रैपिंग ईमेल युग की विरासत है, जब मैसेज फ़ॉर्मेट की लाइन लंबाई सीमा तय करती थी। वह नंबर हर चीज़ के तेज़-तेज़ होने के तीन दशकों को पार कर चुका है।
  • "Data URI" एक हटा दिया गया नाम है। WHATWG ने बड़े URI-से-URL सामंजस्य के दौरान इसे "data URL" नाम दिया, इसलिए स्पेसिफ़िकेशन, ब्लॉग पोस्ट्स, और पैकेज नाम सब एक ही अनुच्छेद में अलग-अलग स्पेलिंग करते हैं।
  • btoa('') '' लौटाता है: ख़ाली इनपुट से ख़ाली आउटपुट, न पैडिंग, न ख़ास मामला - चरों की संख्या शून्य वाली एकमात्र Base64 स्ट्रिंग (उसकी लंबाई, 0, भी चार का गुणज है)।
  • कैनवास toDataURL से फ़ोटो को डेटा URL में बदल सकता है - वह क्षमता IE 9, Firefox 2 और Safari 4 से मौजूद है, वेब प्लेटफ़ॉर्म के ज़्यादतर हिस्से से भी पुरानी जिसे हम "आधुनिक" समझते हैं - और <img> टैग और FileReader से उसे रउंड ट्रिप करके वापस ला सकता है।
  • WebSocket हैंडशेक SHA-1(key + 258EAFA5-E914-47DA-95CA-C5AB0DC85B11) को Base64 में एन्कोड करता है, और GUID RFC में एक फिक्स्ड कन्स्टेंट है, जिसका चयन ठीक इसीलिए कि कोई सादा HTTP सर्वर कभी ग़लती से हैंडशेक पूरा न कर सके।

अब आगे क्या करें

ब्राउज़र में एन्कोडिंग की पूरी कला एक पेज पर समा जाती है: btoa सादे, सिंगल-बाइट मामलों के लिए जिनके लिए वह पैदा हुआ; किसी भी ब्राउज़र पर असली टेक्स्ट और फ़ाइलों के लिए TextEncoder प्लस चंकड पुल; आधुनिक, सीधे रास्ते के लिए Uint8Array.toBase64 अपनी वर्णमाला और पैडिंग ऑप्शन के साथ; और URL-सुरक्षित वैरिएंट, पैडिंग के साथ या बिना, जो कुछ भी URL में रहने वाला है। बाक़ी सब अदालीस है: 33 फ़ीसदी का कर भुगतान से पहले उसे जान लें, बाइट्स बड़े रहते उन्हें टाइप्ड ऐरे में रखें, पहले एन्कोड और दूसरा रैप, और सिरीलाइज़ेशन फ़ॉर्मैट को कभी ताला न कहें। जब चैनल राव बाइट्स ले सकता है, तो बाइट्स ही लें - Base64 उन रास्तों के लिए है जहाँ सिर्फ़ प्रिंटेबल टेक्स्ट को प्रवेश मिलता है, और अब आपको ठीक-ठीक मालूम है कि टोल कैसे चुकाया जाता है।

यात्रा का दूसरा आधा हिस्सा - इनमें से एक स्ट्रिंग को मिलाना और उसमें से बाइट्स, टेक्स्ट, और अर्थ वापस बाहर निकालना - विस्तार से JavaScript में Base64 डिकोडिंग के संगी गाइड में कवर किया गया है, जो नीचे लिंक किया गया है।

अंतिम अपडेट: 2026-09-08

संबंधित लेख: JavaScript/Browser में Base64 डिकोडिंग: एक सम्पूर्ण गाइड