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

C# (CSharp) में Base64 एन्कोडिंग: एक सम्पूर्ण गाइड

बाइट्स आपके पास हैं। एक PNG जिसे JSON रिस्पॉन्स के अंदर सफ़र करना है, एक टोकन जो URL में फिट होना चाहिए, एक पंक्ति टेक्स्ट जो ऐसे सिस्टम में आने वाली है जो सिर्फ़ अक्षर और अंक मानता है। byte[] जो आपके हाथ में है और वह चैनल जिसे उसे पार करना है, इन दोनों के बीच C# एक Base64 एन्कोडर्स का मेन्यू पेश करता है, और इनमें से चुनना ही इस विषय की असली हुनर है। क्लासिक वन-लाइनर 2003 से फ्रेमवर्क में है, स्पैन-आधारित और URL-सुरक्षित विकल्प आधुनिक रनटाइम के साथ आए, और हर एक साइज़, लाइन ब्रेक्स और वर्णमाला पर अलग-अलग वादे करता है। यह लेख पूरा मेन्यू पढ़ता है, हर उस असली काम के लिए चालू उदाहरणों के साथ जिसे किसी एन्कोडर से माँगा जाता है।

पहले घर के नियम, एक साँस में, क्योंकि इस साइट का होम पेज इस फ़ॉर्मैट को गहराई से समझाता है: एन्कोडर हर तीन बाइट्स को उठाता है और 64-चिह्न वर्णमाला से चार चर लिखता है, पूंछ में एक-दो = चरों से पैड करके, ताकि आपका डेटा आने से करीब 33 प्रतिशत मोटा हुआ बाहर निकले। वह संख्या, कोई कोड नहीं, इस लेख का सबसे ज़रूरी तथ्य है, और नीचे सब कुछ उसके समझदारी से भुगतने के बारे में है।

एन्कोडर का मेन्यू: अपना औज़ार चुनें

यहाँ .NET दुनिया में एन्कोडिंग वाली API का पूरा परिवार है, हर एक किस परिस्थिति के लिए बनी है। सब कुछ रनटाइम के अपने पास है, सिवाय URL-सुरक्षित क्लास के, जो पुराने फ्रेमवर्क्स पर एक छोटे से NuGet पैकेज के साथ आती है:

API कब से उपलब्ध इसे किस काम के लिए
Convert.ToBase64String(byte[]) .NET Framework 1.1 (2003) क्लासिक। पूरा ऐरे अंदर, पैडेड स्ट्रिंग बाहर। न विकल्प, न सरप्राइज़।
Convert.ToBase64String(byte[], int, int) .NET Framework 1.1 (2003) बड़े ऐरे का हिस्सा एन्कोड करें, पहले उसे बाहर कॉपी किए बिना।
Convert.ToBase64String(byte[], Base64FormattingOptions) .NET 2.0 (2005) डायल वाला क्लासिक: इच्छा हो तो हर 76 चरों पर लाइन ब्रेक डालें, MIME के ढंग से।
Convert.ToBase64String(ReadOnlySpan<byte>, Base64FormattingOptions) .NET Core 2.1 (2018) स्पैन वाला रूप: बफ़र का एक व्यू एन्कोड करें, न ऐरे कॉपी, न हिस्से की अलोकेशन।
Convert.ToBase64CharArray(byte[], int, int, char[], int) .NET Framework 1.1 (2003) अपने चर बफ़र में लिखें, और वापस पता चले कि कितने चर इस्तेमाल हुए।
Convert.TryToBase64Chars(ReadOnlySpan<byte>, Span<char>, out int, ...) .NET Core 2.1 (2018) एक्सेप्शन की जगह Boolean: बफ़र में फिट हो तो एन्कोड करें, न हो तो false रिपोर्ट करें।
System.Buffers.Text.Base64.EncodeToUtf8, EncodeToUtf8InPlace .NET Core 2.1 (2018) सख़्त स्पैन परिवार: एक्सेप्शन की जगह स्टेटस कोड्स, और उन बफ़र्स के लिए इन-प्लेस इन्फ्लेशन जो आप पहले से पकड़े हैं।
System.Buffers.Text.Base64Url.EncodeToString और उसके भाई-चारे .NET 9 (2024) URL-सुरक्षित वर्णमाला, बिना पैडिंग के। .NET Framework 4.6.2+ और .NET Standard 2.0 पर: Microsoft.Bcl.Memory NuGet पैकेज।
ToBase64Transform + CryptoStream .NET Framework 1.1 (2003) स्ट्रीमिंग: फ़ाइल को बहते-बहते एन्कोड करें, पूरा पेलोड कभी भी मेमोरी में न रखें।

अगर आपका प्रोजेक्ट 2018 के बाद के किसी .NET वर्ज़न को टारगेट करता है, तो पहले सात पंक्तियाँ और नीचे वाला स्ट्रीमिंग जोड़ी बॉक्स में पहले से हैं। Base64Url को .NET 9 या उससे नया ज़रूरत है, या उससे पुरानी किसी भी चीज़ पर Microsoft.Bcl.Memory पैकेज। और आगे की एक नोट: .NET 11 लाइब्रेरियाँ, इस लेख लिखते समय प्रिव्यू में हैं, सामान्य रिलीज़ 2026 के अंत में उम्मीद, मौजूदा टाइप्स में और Base64 सुविधा API और ओवरलोड जोड़ती हैं, तो यह मेन्यू बढ़ता ही जाएगा। इस लेख में बाक़ी कोई पैकेज ज़रूरी नहीं।

स्टैंडर्ड कॉल: Convert.ToBase64String

C# की एन्कोडिंग वाली ज़िंदगी का नब्बे प्रतिशत एक ही कॉल है। इसे बाइट्स दें, और वह वही स्ट्रिंग वापस सौंपेगी जो उन्हें ढोती है:

using System;
using System.Text;

string text = "Man";
byte[] bytes = Encoding.UTF8.GetBytes(text);
string packed = Convert.ToBase64String(bytes);
Console.WriteLine(packed);
// TWFu

दो कदमों के उस रूप पर ध्यान दें, क्योंकि C# में "मेरा Base64 मैच क्यों नहीं होता" सवाल का सबसे आम जवाब यहीं छिपा है। कोई ऐसा ओवरलोड नहीं है जो सीधे string लें, और यह डिज़ाइन से है: C# स्ट्रिंग UTF-16 होती है, और फ्रेमवर्क यह अंदाज़ा लगाने से इनकार कर देता है कि "इस टेक्स्ट को एन्कोड करो" कहते समय आप कौन से बाइट्स मतलब रखते हैं। आप पहले बाइट का प्रतिनिधित्व चुनते हैं, Encoding.UTF8.GetBytes से (या डेटा वाक़ई भी चाहे कैरेक्टर सेट से), और तभी Base64 वाला कदम होता है। क्लासिक परिवार का बाक़ी हिस्सा वही कॉल है, बस कमर ज़्यादा टाइट: (byte[], int, int) ओवरलोड बफ़र के हिस्से को एन्कोड करता है, हिस्से को बाहर कॉपी किए बिना, और स्पैन ओवरलोड ReadOnlySpan<byte> से वही करता है, जो वही औज़ार है जब डेटा किसी बड़े रीड बफ़र की एक खिड़की हो। क्लासिक एन्कोडर का एक गुण सीधा कहने लायक है: वह कभी फेल नहीं होता और कभी पूछता-तांका नहीं करता। वह हमेशा स्टैंडर्ड वर्णमाला देता है, हमेशा पैडिंग शामिल करता है, और एक जैसे इनपुट के लिए हमेशा वही स्ट्रिंग देता है, इसलिए Base64 स्ट्रिंग वही बाइट्स की भरोसेमंद फिंगरप्रिंट है जिन्होंने उसे बनाया।

76-चर का सवाल: लाइन ब्रेक्स और Base64FormattingOptions

क्लासिक एन्कोडर पर एक डायल है, और वह .NET 2.0 से वहीं है: Base64FormattingOptions। इसे InsertLineBreaks पर रखें, तो एन्कोडर आउटपुट के हर 76 चरों के बाद एक लाइन ब्रेक डालता है, वही पंक्ति-लंबाई जो MIME विनिर्देश ईमेल एटैचमेंट्स के लिए इस्तेमाल करता है। इसे None पर रखें, या विकल्प-रहित ओवरलोड्स इस्तेमाल करें, तो एक लंबी, बिना टूटी स्ट्रिंग मिलती है:

using System;

byte[] bytes = new byte[90];
string plain = Convert.ToBase64String(bytes);
string wrapped = Convert.ToBase64String(bytes,
  Base64FormattingOptions.InsertLineBreaks);

Console.WriteLine(plain.Length);   // 120
Console.WriteLine(wrapped.Length); // 122, चर 76 के बाद एक लाइन ब्रेक जुड़ा

उस डायल की दो बारीकियाँ अमल में मायने रखती हैं। पहली, जो लाइन ब्रेक वह डालता है वह Windows का जोड़ा है, कैरिएज रिटर्न प्लस लाइन फ़ीड, अकेली लाइन फ़ीड नहीं। तो रैप्ड आउटपुट में \r\n सिक्वेंसेस होती हैं, और कोई भी कोड जो बाद में स्ट्रिंग की "सफ़ाई" के लिए सिर्फ़ \n हटाए, उसे डेटा में छुपे हुए भटकते कैरिएज रिटर्न मिलेंगे। दूसरी, रैप एन्कोडेड आउटपुट के 76 चरों पर होता है, और इसीलिए MIME मानक गारंटी दे सकता था कि ईमेल ट्रांसपोर्ट, अपने 76-या-78 चरों की पंक्ति-सीमाओं के साथ, कभी चारों-के-समूह को पंक्तियों में नहीं बाँटेगा: 76, 4 की गुणज है, इसलिए हर पंक्ति समूह की सीमा पर ख़त्म होती है। जब आप ईमेल बॉडी, PEM-शैली टेक्स्ट ब्लॉक, या कोई भी ऐसी चीज़ बना रहे हों जिसे लीगेसी मेल पाइपलाइन ढोएगा, तब रैप्ड रूप चाहिए। बाक़ी हर जगह अनरैप्ड रूप चाहिए: JSON पेलोड्स, URL टोकन, API रिस्पॉन्स, और ऐसी फ़ाइलें जिन्हें सख़्त पार्सर डिकोड करेगा जो सरप्राइज़ से नफ़रत करता है। और JWT के अंदर रैप्ड रूप कभी नहीं चाहिए, जहाँ विनिर्देश लाइन ब्रेक्स, व्हाइटस्पेस, और पैडिंग तक को स्पष्ट रूप से मना करता है।

आउटपुट पर काबू: चर बफ़र्स और Try API

कभी-कभी स्ट्रिंग लक्ष्य नहीं होता, बफ़र होता है। आप किसी निश्चित-साइज़ चर ऐरे में जोड़ रहे हैं, किसी प्रोटोकॉल फ्रेम में लिख रहे हैं, या बस रनटाइम को आउटपुट के लिए अलोकेशन करने से नहीं चाहते। उन पलों के लिए एन्कोडर के पास 1.1 के दिनों से चर-बफ़र मोड है, और Span के दौर से Try मोड। चर-बफ़र वाला मीथड उस ऐरे में लिखता है जो आप देते हैं, और बताता है कि वह कितने चर इस्तेमाल किए, इसलिए बफ़र की साइज़ लगाना आपका काम है, और स्टैंडर्ड लाइब्रेरी तो साइज़ का फ़ॉर्मूला भी सौंप देती है:

using System.Buffers.Text;
using System.Text;

byte[] bytes = Encoding.ASCII.GetBytes("Man");
char[] buffer = new char[Base64.GetMaxEncodedToUtf8Length(bytes.Length)];
int written = Convert.ToBase64CharArray(bytes, 0, bytes.Length, buffer, 0);

string packed = new string(buffer, 0, written);
Console.WriteLine(packed);
// TWFu

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

using System;

byte[] bytes = { 1, 2, 3 };
char[] buffer = new char[4];

if (Convert.TryToBase64Chars(bytes, buffer, out int written,
  Base64FormattingOptions.None))
{
  Console.WriteLine(new string(buffer, 0, written));
  // AQID
}
else
{
  Console.WriteLine("Buffer too small, nothing was written.");
}

System.Buffers.Text.Base64 के सख़्त Span परिवार के लिए, वही रूप OperationStatus करार के साथ मौजूद है, Boolean के बजाय: EncodeToUtf8 उस बाइट स्पैन को भरता है जो आपका है, और स्टेटस से बताता है कि वह ख़त्म हुआ, जगह ख़त्म कर बैठा, या और इनपुट चाहिए, और EncodeToUtf8InPlace वही औज़ार है जब बाइनरी डेटा पहले से किसी ऐसे बफ़र में बसा हो जिसे आप बढ़ने दें: एन्कोडिंग डेटा को फुलाती है, इसलिए मीथड Base64 टेक्स्ट उसी बफ़र के आख़िरी हिस्से पर लिखती है और बताती है कि रिज़ल्ट कितना लंबा है। इन सबकी साइज़िंग पर एक ही नियम लागू है: n इनपुट बाइट्स का आउटपुट हमेशा 4 * ceil(n / 3) चर होता है, पैडिंग समेत, और GetMaxEncodedToUtf8Length तथा Base64Url.GetEncodedLength हेल्पर उसी हिसाब-किताब को लागू करते हैं - आख़िरी वाला बिना-पैडिंग लंबाई के लिए, जो हमेशा पैडेड साइज़ से छोटी या बराबर होती है - इसलिए साइज़ हेल्परों से लगाएँ, याद की गई किसी स्थिरांक से नहीं।

URL-सुरक्षित एन्कोडर: Base64Url

स्टैंडर्ड वर्णमाला में दो ऐसे चर हैं जो URL पसंद नहीं करते। क्वेरी स्ट्रिंग में + फ़ॉर्म-पार्सिंग के नियमों के तहत आमतौर पर स्पेस की तरह डिकोड हो जाता है, और / और = दोनों को पैथ या पैरामीटर में सवारी से पहले परसेंट-एन्कोडिंग की ज़रूरत होती है। Base64 का URL-सुरक्षित वेरिएंट, जिसे RFC 4648 के सेक्शन 5 में परिभाषित किया गया है, + और / को बदलकर - और _ देता है, जो कहीं भी एस्केपिंग के मोहताज नहीं, और आख़िरी = पैडिंग को वैकल्पिक बना देता है। .NET 9 से रनटाइम के लिए इसके लिए एक समर्पित क्लास है, System.Buffers.Text.Base64Url, और इसका एक ऐसा व्यवहार है जो लोगों को पहली बार सरप्राइज़ देता है: वह पैडिंग बिल्कुल नहीं देता:

using System.Buffers.Text;

byte[] bytes = { 1, 2 };
string classic = Convert.ToBase64String(bytes);
string urlSafe = Base64Url.EncodeToString(bytes);

Console.WriteLine(classic); // AQI=
Console.WriteLine(urlSafe); // AQI

वही फ़र्क़ पूरा मक़सद है। JWT सेगमेंट, अपलोड पहचान चिह्न, क्वेरी स्ट्रिंग में टोकन, URL पैथ में वैल्यू: इन सबको बिना-पैडिंग URL-सुरक्षित रूप चाहिए, और Base64Url.EncodeToString सीधे देता है, वर्णमाला और पैडिंग दोनों को उन फ़ॉर्मैट्स के तय किए ढंग से संभालकर। इस क्लास में पूरा परिवार है, स्ट्रिंग में एन्कोड, चर स्पैन में, और UTF-8 बाइट स्पैन में, साथ में बफ़र साइज़िंग के लिए GetEncodedLength और आने वाले इनपुट को वैलिडेट करने के लिए IsValid। अगर आपका प्रोजेक्ट पुराने रनटाइम पर चलता है, तो Microsoft.Bcl.Memory पैकेज जोड़ लें, जिसे Microsoft इस क्लास को .NET Framework 4.6.2 और ऊपर बैकपोर्ट करने के लिए ही publish करता है:

dotnet add package Microsoft.Bcl.Memory

और अगर पैकेज इस्तेमाल नहीं कर सकते, तो हाथ-से-बनाया रूप क्लासिक एन्कोडर प्लस दो रिप्लेस और एक ट्रिम है, जो आपको बहुत सारे C# कोडबेस में मिलेगा:

using System;
using System.Text;

byte[] bytes = Encoding.UTF8.GetBytes("Hello World!");
string packed = Convert.ToBase64String(bytes)
  .Replace('+', '-')
  .Replace('/', '_')
  .TrimEnd('=');

Console.WriteLine(packed);
// SGVsbG8gV29ybGQh, URL-सुरक्षित और बिना पैडिंग

उस ज़ंजीर में कामों की क्रम-क्रमता ध्यान देने लायक है: चरों का आदान-प्रदान स्टैंडर्ड आउटपुट पर होता है, और पैडिंग आख़िर में कटी जाती है, क्योंकि पहले काटने से कुछ नहीं बदलता बस कोड पढ़ने में मुश्किल हो जाता है, और काटने के बाद बदलने से भी काम चलता लेकिन बारीक बग इसी ढंग से जन्म लेते हैं। यह रूप टोकन, पहचान चिह्न, और हर उस चीज़ के लिए रखें जो URL में बसेगी, और स्टैंडर्ड वर्णमाला ईमेल बॉडी, JSON पेलोड्स और फ़ाइलों के लिए आरक्षित रखें, जहाँ +, / और = बिल्कुल घर-जैसे हैं।

एन्कोडर को क्या खिलाना है: स्ट्रिंग्स, कैरेक्टर सेट्स और एन्कोडिंग का फैसला

हर एन्कोडिंग वाला काम जो टेक्स्ट से शुरू होता है, उसी चुप फैसले से शुरू होता है: यह टेक्स्ट कौन से बाइट्स बनेगा? Base64 वाला कदम निश्चित और निर्दोष है, लेकिन उसके पहले वाला Encoding कदम ही वहाँ है जहाँ आउटपुट अलग हो जाते हैं, और वह अलगाव चुपचाप भी हो सकता है। आधुनिक वेब पर UTF-8 डिफ़ॉल्ट मान है, और यहाँ सही डिफ़ॉल्ट भी: वह हर भाषा को रउंड ट्रिप करता है, यह वह चीज़ है जो हर दूसरी प्लेटफ़ॉर्म आपके पेलोड को डिकोड करते समय मान लेगी, और बस एक कॉल में Encoding.UTF8 से मिलने वाली चीज़ भी:

using System;
using System.Text;

string original = "h\u00e9llo \u4e16\u754c";
byte[] utf8 = Encoding.UTF8.GetBytes(original);
string packed = Convert.ToBase64String(utf8);
Console.WriteLine(packed);
// aMOpbGxvIOS4lueVjA==

अब देखें वही चर एक अलग कैरेक्टर सेट से होकर एन्कोड होता हुआ, और समझें कि कैरेक्टर सेट के बिना "वही टेक्स्ट" एक ठीक-से-परिभाषित चीज़ नहीं है:

using System;
using System.Text;

string euro = "\u20ac";
string asUtf8 = Convert.ToBase64String(Encoding.UTF8.GetBytes(euro));
string asLatin1 = Convert.ToBase64String(
  Encoding.GetEncoding("ISO-8859-1").GetBytes(euro));

Console.WriteLine(asUtf8);   // 4oKs
Console.WriteLine(asLatin1); // Pw==

वही यूरो चिह्न के लिए दो अलग Base64 स्ट्रिंग्स, दोनों बिल्कुल वैध, और सिर्फ़ एक ही दूसरी तरफ वापस यूरो चिह्न में डिकोड होगी। सबसे चौड़े विस्फोट-क्षेत्र वाला फँसाव Encoding.Default है: Windows पर .NET Framework में यह सिस्टम की ANSI code page है, जबकि .NET (Core) में यह UTF-8, तो Encoding.Default से एन्कोड करने वाला प्रोग्राम 2010 की मशीन पर 2025 की मशीन से अलग Base64 बनाता है, और दोनों आउटपुट अपने-अपने घर की प्लेटफ़ॉर्म पर "सही" डिकोड होते हैं। अगर डिकोड हुआ पेलोड मोज़ीबेक से भरा आए, तो मूल एन्कोडिंग ने डिकोड के माने से अलग कैरेक्टर सेट इस्तेमाल किया था, और ठीक पाइप की इसी तरफ है: एन्कोडिंग को स्पष्ट रूप से पिन करें, दोनों दिशाओं में, उस कोड में जो उस टीम से ज़्यादा ज़िंदा रहेगा जो उसे लिखी। और टाइप सिस्टम खुद पर आख़िरी नोट: C# स्ट्रिंग UTF-16 होती है, तो अगर कभी भी कच्चे UTF-16 कोड यूनिट्स एन्कोडर में भेजें (Encoding.Unicode.GetBytes कॉल करके), हर ASCII चर दो बाइट्स का ख़र्च करता है और आपका आउटपुट साइज़ में दुगना हो जाता है बिना किसी फ़ायदे, क्योंकि दूसरी तरफ का डिकोडर उसे UTF-16 टेक्स्ट के रूप में पढ़ेगा, आपकी मूल स्ट्रिंग के बाइट्स के रूप में नहीं। Base64 वह बाइट्स ढोता है जो आप देते हैं, और उसे कोई फ़र्क़ नहीं पड़ता कि वे क्या मतलब रखते हैं।

फ़ाइलें: डिस्क से स्ट्रिंग तक

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

using System.IO;

byte[] bytes = File.ReadAllBytes("photo.png");
string packed = Convert.ToBase64String(bytes);
File.WriteAllText("photo.b64", packed);

Console.WriteLine(packed.Length + " characters for "
  + bytes.Length + " bytes of image.");

साइज़ का हिसाब-किताब ही पूरी कहानी है, और उसे ट्रांसपोर्ट चुनने से पहले करना चाहिए। फ़ाइल का एक मेगाबाइट Base64 के 1,333,336 चर बन जाता है, और C# स्ट्रिंग हर चर को दो बाइट्स में स्टोर करती है, तो वह एन्कोडेड रिज़ल्ट मेमोरी में स्ट्रिंग के रूप में लगभग 2.7 मेगाबाइट जगह लेता है। दस मेगाबाइट की फ़ाइल बनती है 13 मेगाबाइट की स्ट्रिंग, जो 26 मेगाबाइट मैनेज्ड मेमोरी में बसी होती है। यह सब किसी फोटो या कॉन्फ़िग ब्लॉब के लिए कोई समस्या नहीं, और अगर पेलोड वीडियो हो तो नीचे वाला स्ट्रीमिंग एन्कोडर इस्तेमाल करने का बहुत अच्छा कारण। ऊपर वाला पैटर्न वही है जो मेमोरी में आराम से फिट होने वाली हर चीज़ के लिए, और यह वही पैटर्न है जो हर "JSON बॉडी में फ़ाइल Base64 की तरह अपलोड करो" वाली सुविधा चुपचाप इस्तेमाल करती है: फ़ाइल पढ़ो, एन्कोड करो, स्ट्रिंग JSON में रखो, और API लेयर को अपना काम करने दो।

वेब पर इमेजेस: डेटा URI बनाना

एन्कोडेड इमेजेस का सबसे दृश्यमान कन्स्यूमर वेब है, और वेब का "दस्तावेज़ के अंदर बसने वाली इमेज" का फ़ॉर्मैट ही डेटा URI है: data: स्कीम, फिर MIME टाइप, ;base64 फ़्लैग, एक कमा, और एन्कोडेड बाइट्स। C# में बनाना सिर्फ़ स्ट्रिंग जोड़ना है, और एन्कोडर ही सारा असली काम करता है:

using System.IO;

byte[] png = File.ReadAllBytes("logo.png");
string packed = Convert.ToBase64String(png);
string dataUri = "data:image/png;base64," + packed;

Console.WriteLine(dataUri.Substring(0, 30));
// data:image/png;base64,iVBORw0K

उस आउटपुट में iVBORw0KGgo प्रिफिक्स एक उपयोगी चेकपॉइंट है: यह 8-बाइट PNG सिग्नेचर का Base64 रूप है, तो आप जो भी PNG एन्कोड करेंगे उसकी शुरुआत इसी से होगी, और कोई भी PNG डेटा URI जो इससे शुरू न हो वह PNG नहीं है। इस पैटर्न के साथ तीन प्रैक्टिकल नोट्स लगते हैं। पहला, डेटा URI इमेज की पूरी कॉपी है, एक-तिहाई बढ़ाकर, आपके HTML या CSS में बसी, तो वह एक नेटवर्क रिक्वेस्ट को हमेशा की पेज वेट से बदलता है, 4 KB फ़ेविकॉन के लिए सौदा और 4 MB हीरो इमेज के लिए चुकाने वाला सौदा, और एन्कोडर 33 प्रतिशत पर तर्क-वितर्क नहीं करता। दूसरा, इमेज बड़ी हो तो एन्कोड से पहले रिज़ाइज़ या रिकम्प्रेस कर लें, क्योंकि मूल की हर एक बाइट पेज पर नज़र आती है। तीसरा, यूज़र-फ़ेसिंग HTML में यूज़र से मिली SVG से सावधानी रखें: SVG स्क्रिप्ट ढो सकता है, तो उसे embed करना, इनलाइन या <object>/<embed> के ज़रिए, क्लासिक XSS सरफ़स है। साधारण <img> डेटा URI उसे मॉडर्न ब्राउज़र्स में चलाएगा नहीं, लेकिन वही मार्कअप उन कॉन्टेक्स्ट्स में दोबारा इस्तेमाल हुआ तो चलाएगा। PNG, JPEG, GIF और WebP के डेटा URI निष्क्रिय हैं; SVG वही एक है जो नहीं।

हाथ से JWT जोड़ना

खाली से एक JSON Web Token बनाना एक रीत है, और C# में यह रीत ज़्यादातर भाषाओं से बेहतर है, क्योंकि इसके टुकड़े छोटे हैं। JWT तीन base64url सेगमेंट्स हैं, डॉट से जुड़े: एन्कोडेड हेडर, एन्कोडेड पेलोड, और सिग्नेचर। पहले दो UTF-8 JSON दस्तावेज़ हैं, और सिग्नेचर उन दो सेगमेंट्स पर कंप्यूट होता है, जो डॉट से जुड़े हों। यहाँ पूरी एसेंबली है, स्टैंड-इन सिग्नेचर के साथ, क्योंकि क्रिप्टोग्राफिक कदम आपकी signing key का है, Base64 की कहानी का नहीं:

using System;
using System.Buffers.Text;
using System.Text;

string headerJson = "{\"alg\":\"HS256\",\"typ\":\"JWT\"}";
string payloadJson = "{\"sub\":\"42\",\"name\":\"Ada\"}";

string header = Base64Url.EncodeToString(Encoding.UTF8.GetBytes(headerJson));
string payload = Base64Url.EncodeToString(Encoding.UTF8.GetBytes(payloadJson));
string signature = "c2lnbmF0dXJl"; // असली HMAC या ECDSA वैल्यू की जगह का स्टैंड-इन

string jwt = header + "." + payload + "." + signature;
Console.WriteLine(jwt);
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI0MiIsIm5hbWUiOiJBZGEifQ.c2lnbmF0dXJl

उस उदाहरण में Base64Url.EncodeToString के दो गुण चुपचाप काम कर रहे हैं। यह URL-सुरक्षित वर्णमाला देता है, तो न + टोकन में आ सकता है न /, और यह पैडिंग छोड़ता है, तो = भी कभी नहीं आता, जो कि JWS विनिर्देश का पूरा मक़सद है, और वही काम Convert.ToBase64String मदद के बिना नहीं करेगा। अगर आप .NET 9 से पहले के रनटाइम पर हैं, तो वही काम स्टैंडर्ड एन्कोडर प्लस URL-सुरक्षित सेक्शन वाली फिक्स-अप ज़ंजीर से चलता है: एन्कोड करें, दो चर बदलें, पैडिंग काटें। सेगमेंट्स की क्रम-क्रमता सिग्नेचर के लिए मायने रखती है, क्योंकि यह header प्लस डॉट प्लस payload पर कंप्यूट होता है, सादे ASCII बाइट्स की तरह, तो पहले दोनों सेगमेंट्स जोड़ें और उनकी बिल्कुल वही कॉन्कटनेशन पर साइन करें, JSON की किसी दोबारा फ़ॉर्मेट की गई नक़ल पर नहीं। और एक सीमा सख़्त रखें: किसी भी ऐसे काम के लिए जिस तक यूज़र पहुँच सकता है, JWT हाथ से बिल्कुल न बनाएँ। System.IdentityModel.Tokens.Jwt पैकेज बनाना, साइन करना, वैलिडेशन और एक्सपायर सब आपके लिए संभालता है, और उसकी base64url हैंडलिंग बस यही वर्णमाला-पैडिंग नियम है। हाथ से जोड़ना टेस्ट्स, डेमो, और उस दिन के लिए है जब आपको बिल्कुल पता होना हो कि लाइब्रेरी क्या कर रही है।

HTTP हेडर्स: Basic Auth

Base64 सादा HTTP में Basic authentication स्कीम में सामने आता है, और एन्कोडिंग तरफ़ प्रोटोकॉल के सबसे छोटे हेडर बिल्डर में से एक है: यूज़रनेम और पासवर्ड को कॉलन से जोड़ें, रिज़ल्ट को UTF-8 में एन्कोड करें, Base64 करें, और स्कीम नाम से प्रीफ़िक्स करें:

using System;
using System.Text;

string user = "ada";
string password = "s3cret";
string credentials = user + ":" + password;

string header = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(credentials));
Console.WriteLine(header);
// Basic YWRhOnMzY3JldA==

कैरेक्टर सेट ही उलझन वाला हिस्सा है: RFC 7617, बैकवर्ड कॉम्पाटिबिलिटी के लिए, Basic स्कीम की डिफ़ॉल्ट कैरेक्टर सेट अपरिभाषित छोड़ता है और सिर्फ़ UTF-8 की एक advisory झलक देता है, लेकिन यही हर मॉडर्न सर्वर की उम्मीद है, तो एक्सेंटेड यूज़रनेम को Encoding.UTF8 से गुज़ारना चाहिए, प्लेटफ़ॉर्म डिफ़ॉल्ट नहीं, वरना सर्वर एक अलग बाइट स्ट्रिंग डिकोड करेगा और लॉगिन अस्वीकार कर देगा। Base64 वाला कदम ही हेडर में एन्कोडिंग है: रिज़ल्ट को परसेंट-एन्कोड न करें, न URL-एन्कोड न करें, न डबल-Base64 न करें। इनमें से हर "मददगार" एक्सट्रा स्टेप एक ज़ाना बग है, और डबल-एन्कोडिंग वाला सबसे आम, क्योंकि क्रेडेंशियल्स कभी-कभी पहले से एन्कोडेड आते हैं किसी ऐसी लेयर से जो उन्हें पहले ही Base64 कर चुकी थी, और दूसरी एन्कोडिंग एक ऐसा हेडर बनाती है जो सही लगता है और सर्वर पर चुपचाप फेल होता है। स्कीम खुद पर दो सतर्कताएँ, ताकि वे यहीं पड़ें, सुरक्षा सेक्शन में नहीं जहाँ वे पतली पड़ जातीं: Basic auth पासवर्ड को ऐसे रूप में भेजता है जो एक कमांड दूर पढ़ने लायक है, तो यह सिर्फ़ TLS के ऊपर मंज़ू है, और तब भी ज़्यादातर API काम के लिए गलत औज़ार है, इसीलिए बियरर टोकन और JWTs ने जगह ली। इस सबमें एन्कोडर का काम छोटा और ईमानदार है: कॉलन-जुड़े क्रेडेंशियल्स को एक हेडर-सेफ़ स्ट्रिंग में बदलना, और कुछ और नहीं।

ईमेल: MIME और ToBase64Transform रैप क्यों नहीं करता

ईमेल Base64 का इतिहासिक घर है, और अभी भी यही वह जगह है जहाँ से 76-चर वाला पंक्ति-नियम आया है: MIME विनिर्देश एन्कोडेड बॉडी को 76 चरों पर रैप करता है, पंक्तियों के बीच CRLF के साथ, ताकि किसी SMTP हॉप को उन्हें दोबारा रैप करने का बहाना न मिले। C# इस काम के लिए दो एन्कोडर देता है, और दोनों अलग वादे करते हैं, जो चुनने से पहले समझने लायक हैं। पहला क्लासिक Convert.ToBase64String है InsertLineBreaks के साथ, जो आपने लाइन-ब्रेक सेक्शन में देखा, और यह बिल्कुल MIME का रूप है, 76 पर CRLF के साथ रैप्ड, Content-Transfer-Encoding: base64 हेडर के नीचे पेस्ट करने के लिए तैयार। दूसरा ToBase64Transform है, स्ट्रीमिंग रिश्तेदार, और यही है सरप्राइज़: वह लाइन ब्रेक्स नहीं डालता। न उनका कोई मोड, न कोई ऑप्शन, न कोई कॉन्स्ट्रक्टर फ़्लैग, और उसका आउटपुट एक लंबी अनरैप्ड स्ट्रीम है:

using System.IO;
using System.Security.Cryptography;

using FileStream source = File.OpenRead("photo.png");
using MemoryStream destination = new MemoryStream();
using ToBase64Transform transform = new ToBase64Transform();
using CryptoStream encoder = new CryptoStream(source, transform, CryptoStreamMode.Read);

encoder.CopyTo(destination);
Console.WriteLine(destination.Length + " characters, no line breaks");

तो प्रैक्टिकल नियम है: छोटे-मध्यम ईमेल पेलोड्स के लिए बाइट्स पढ़ें और रैपिंग वाला क्लासिक एन्कोडर इस्तेमाल करें, क्योंकि आपको सीधे MIME रूप मिलता है। बड़े एटैचमेंट्स के लिए ToBase64Transform से स्ट्रीम करें, मेमोरी सपाट रखें, और अगर ट्रांसपोर्ट को सच में 76-चर की पंक्तियाँ चाहिए, तो रिज़ल्ट को खुद रैप करें, आउटपुट को समूह सीमाओं पर बाँटकर (हर 76 चर हमेशा एक समूह सीमा है, जैसा लाइन-ब्रेक सेक्शन में समझाया गया)। ट्रांसफ़ॉर्म अनरैप्ड रहकर सही काम कर रहा है: वह इनपुट को 3 बाइट्स के समूहों में प्रोसेस करता है, और लाइन ब्रेक्स एक फ़ॉर्मेटिंग फैसला है जो उस लेयर का है जो ट्रांसपोर्ट जानती है, उस लेयर का नहीं जो पाइप में बाइट्स को चरों में बदल रही है।

स्ट्रीमिंग: बड़ी फ़ाइलें दो बार पढ़े बिना एन्कोड करना

जब पेलोड वीडियो हो, बैकअप हो, या कोई ऐसी चीज़ जो स्ट्रिंग में पकड़े रहने के लिए शरमाता हो, तो स्ट्रीमिंग एन्कोडर ही पूरा हल है। यह पैटर्न डिकोड-तरफ़ की स्ट्रीमिंग का आरसा है: सोर्स फ़ाइल के ऊपर CryptoStream, रीड मोड में ToBase64Transform, और टारगेट में CopyTo। फ़ाइल अंदर बहती है, Base64 बाहर बहता है, और प्रोसेस जो मेमोरी पकड़े वह सिर्फ़ स्ट्रीम के आंतरिक बफ़र तक सीमित है:

using System.IO;
using System.Security.Cryptography;

using FileStream source = File.OpenRead("video.mp4");
using FileStream target = File.Create("video.b64");
using ToBase64Transform transform = new ToBase64Transform();
using CryptoStream encoder = new CryptoStream(source, transform, CryptoStreamMode.Read);

encoder.CopyTo(target);
Console.WriteLine("Wrote " + target.Length + " characters.");

इस पैटर्न के दो तथ्य याद रखने लायक हैं। पहला, आउटपुट की साइज़ इनपुट की साइज़ से पूरी तरह तय होती है, 3 बाइट्स पर 4 चर, इसलिए आप टारगेट की जगह पहले से रज़र्व कर सकते हैं, content-length हेडर की लंबाई पहले से कंप्यूट कर सकते हैं, या एक बाइट बहने से पहले ही डिस्क क्वॉटा तय कर सकते हैं। दूसरा, ट्रांसफ़ॉर्म अपना इनपुट 3 बाइट्स के समूहों में उम्मीद करता है, और CryptoStream वह एलाइनमेंट आपके लिए संभालता है, फ़ाइल बहते-बहते ट्रांसफ़ॉर्म को बस वह देता है जो वह चाहता है। अगर कभी भी आप TransformBlock से ट्रांसफ़ॉर्म को हाथ से चलाएँ, तो 3 के गुणज दें, और पूंछ को TransformFinalBlock को ख़ाली करने दें, वह एक-दो बचे हुए बाइट्स जो आख़िरी अधूरा समूह बनते हैं, एक-दो पैडिंग चरों के साथ। ज़्यादातर एप्लिकेशंस के लिए CopyTo वाला रूप ही वह सब है जो आपको कभी लिखना होगा, और यही वह रूप है जो मेमोरी-सीमा के नीचे आराम से चलता है, जहाँ बड़ी फ़ाइलें बसना चाहती हैं।

कॉन्फ़िग, एनवायरनमेंट वेरिएबल और डेटाबेसेस

C# एप्लिकेशंस में एक और आम एन्कोडिंग वाला काम है स्टोरेज वाला काम: एक सीक्रेट या बाइनरी ब्लॉब को उठाकर उसे एक ऐसी जगह पर रखना जो सिर्फ़ टेक्स्ट मानती है। एनवायरनमेंट वेरिएबल दृश्यमान उदाहरण है, क्योंकि एनवायर, परिभाषा से, एक स्ट्रिंग है:

using System;
using System.Text;

string secret = "p@ssw0rd+and/symbols";
string packed = Convert.ToBase64String(Encoding.UTF8.GetBytes(secret));
Environment.SetEnvironmentVariable("SECRET_B64", packed);

string back = Encoding.UTF8.GetString(
  Convert.FromBase64String(Environment.GetEnvironmentVariable("SECRET_B64")));
Console.WriteLine(back == secret);
// True

डेटाबेसेस में वही विचार आम तौर पर एक byte[] प्रॉपर्टी के रूप में दिखता है जो एक टेक्स्ट कॉलम को पकड़ना होता है, और Entity Framework Core के पास बस इसके लिए एक बिल्ट-इन यंत्र है, वैल्यू कन्वर्टर, जो हर रीड और राइट पर आपकी एन्कोडिंग और डिकोडिंग फ़ंक्शंस चलाता है:

using Microsoft.EntityFrameworkCore;

modelBuilder.Entity<Avatar>()
  .Property(a => a.ImageData)
  .HasConversion(
    v => Convert.ToBase64String(v),
    v => Convert.FromBase64String(v));

वही कन्वर्टर ही पूरा डेटाबेस इंटीग्रेशन है: C# कोड byte[] देखता है, कॉलम Base64 स्ट्रिंग देखता है, और रउंड ट्रिप कॉल साइट पर अदृश्य है। इस सेक्शन के साथ दो सतर्कताएँ जाती हैं। पहली, कॉलम 33 प्रतिशत की कर दे रहा है: एन्कोडेड लंबाई के लिए साइज़्ड टेक्स्ट कॉलम, बराबर चौड़ाई वाली बाइनरी से एक-तिहाई कम डेटा पकड़ता है, तो अगर आपके पास फिक्स्ड-चौड़ाई कॉलम है, उसे Base64 लंबाई के लिए साइज़ करें, और अगर varchar(max) या तुल्य है, तो कर सिर्फ़ बिलिंग का सवाल है। दूसरी, और यही वह सवाल है जो बार-बार लौट आता है, कॉन्फ़िग फ़ाइल में Base64 एक रूप है, ढाल नहीं। वह वैल्यू को एक पंक्ति में रखता है, टेक्स्ट एडिटर्स के रास्ते से बाहर रखता है, और फ़ाइल पढ़ने वाला कोई भी एक कमांड दूर उसे पढ़ने लायक बना सकता है। सीक्रेट्स को असली सुरक्षा चाहिए, सीक्रेट स्टोर, की-वॉल्ट, कम से कम फ़ाइल परमिशन, और Base64 तो बस वह ट्रांसपोर्ट फ़ॉर्मैट है जो सीक्रेट कॉन्फ़िग में बैठे हुए पहनता है।

कमांड लाइन से

हर एन्कोडर को 15-पंक्ति की कंसोल ज़िंदगी का हक़ है, और C# वाला सुखद है, क्योंकि आउटपुट एक सादी स्ट्रिंग है जिसके लिए स्टैंडर्ड आउटपुट ही बना है। यहाँ पूरा औज़ार है: यह फ़ाइल पैथ या स्टैंडर्ड इनपुट लेता है, एन्कोड करता है, और Base64 को टर्मिनल में लिखता है जहाँ कोई भी शेल पाइपलाइन उसे उठा सकता है:

using System;
using System.IO;
using System.Text;

string input = args.Length > 0
  ? File.ReadAllText(args[0])
  : Console.In.ReadToEnd();

byte[] bytes = Encoding.UTF8.GetBytes(input);
Console.WriteLine(Convert.ToBase64String(bytes));

एक बार बना लें तो वह शेल की अपनी base64 यूटिलिटी के बग़ल में बैठ जाता है, उन दिनों के लिए जब आपको ख़ास .NET एन्कोडर का व्यवहार चाहिए: वही वर्णमाला, वही पैडिंग, और C# रनटाइम की UTF-8 हैंडलिंग, चाहे पाइप क्या भी दे। बाइनरी फ़ाइलों के लिए वही स्कलेटन, File.ReadAllText की जगह File.ReadAllBytes के साथ, पूरा बदलाव है, और आउटपुट तब फ़ाइल के बिल्कुल एक्ज़ैक्ट बाइट्स का वर्णन करता है, उसके टेक्स्ट-अनुवाद का नहीं। यह औज़ार अच्छा प्रोब भी है: एक फ़ाइल को इसमें बिथारें, आउटपुट को डिकोडिंग लेख के डिकोडर से बिथारें, और दोनों फ़ाइलों का डिफ़ लें, जो पाइप के दोनों सिरों के हर बाइट पर आपस में सहमत होने की संतोषजनक एंड-टू-एंड जाँच है।

पैडिंग, यानी आख़िरी बराबर निशान

Base64 स्ट्रिंग के आख़िरी = चर इस फ़ॉर्मेट की बुककीपिंग हैं, और C# के एन्कोडर्स उन पर मतभेद रखते हैं, जो एक ख़ास और आम इंटरऑप बग का स्रोत है। क्लासिक Convert.ToBase64String हमेशा पैड करता है, क्योंकि जिस क्लासिक डिकोडर से वह जोड़ा जाता है वह हमेशा उम्मीद करता है। Base64Url.EncodeToString कभी पैड नहीं करता, क्योंकि URL-सुरक्षित कन्स्यूमर, JWT और टोकन API, जो उसके निशाने पर हैं, हमेशा कॉम्पैक्ट रूप की उम्मीद करते हैं। जब आपका आउटपुट एक ऐसी दुनिया में उतरता है जहाँ उल्टी उम्मीद है, तो ठीक हिसाब-किताब है, और वही हिसाब-किताब है जिसे डिकोडिंग लेख ने उल्टी दिशा के लिए दिखाया था:

using System;

string padded = Convert.ToBase64String(new byte[] { 1, 2 });
Console.WriteLine(padded);           // AQI=
Console.WriteLine(padded.TrimEnd('=')); // AQI, जो URL-सुरक्षित कन्स्यूमर चाहता है

string compact = "AQI";
string restored = compact + new string('=', (4 - compact.Length % 4) % 4);
Console.WriteLine(restored);         // AQI=, जो क्लासिक डिकोडर चाहता है

(4 - length % 4) % 4 फ़ॉर्मूला ही पूरा पैडिंग ब्रह्मांड है: यह लंबाई को 4 का गुणज बनाने के लिए 0, 1, या 2 चर जोड़ता है, और बाहर वाला मोड्यूलो पहले से पैडेड इनपुट को और बढ़ने से रोकता है। पैडिंग पर दो चेतावनियाँ, क्योंकि यहीं अच्छी नीयत का कोड गलत होता है। = को कभी डेटा की तरह न मानें: इसमें कोई जानकारी नहीं है, तो एक ऐसे स्ट्रिंग को एन्कोड करना जो पहले से पैडिंग ले आया हो, जैसे वह पेलोड हो, या क्वेरी स्ट्रिंग में = को URL-एन्कोडिंग करके %3D बनाना, दोनों सही-लगने वाले आउटपुट बनाने के तरिके हैं जो ग़लत डिकोड होते हैं। और उस छोटे परिवार की पुरानी पेलोड्स से सावधान रहें जिनमें पैडिंग एक अलग चर के रूप में लिखी गई थी, कुछ पुराने सिस्टम में डॉट, स्टैंडर्ड = की जगह: अगर मिली वैल्यू जहाँ पैडिंग की उम्मीद है वहाँ डॉट इस्तेमाल करती है, तो डिकोड से पहले उसे वापस = में नॉर्मलाइज़ करें, या उसे बिना-पैडिंग URL-सुरक्षित पैथ से गुज़ारें।

यह कितनी तेज़ी से चलता है

आधुनिक .NET में Base64 एन्कोडिंग तेज़ है, और दिलचस्प हिस्सा मेमोरी की कहानी है, CPU की नहीं। रनटाइम की इम्प्लेमेंटेशन जहाँ हार्डवेयर SIMD वेक्टर इंस्ट्रक्शन सपोर्ट करता है वहाँ उनसे ऑप्टिमाइज़ हैं, और एक साधारण डेस्कटॉप मशीन पर कई-मेगाबाइट के इनपुट एक अंक से दो अंकों के नीचे मिलीसेकंड में एन्कोड हो जाते हैं, इतने तेज़ कि एन्कोडर आपके लिखे किसी भी एप्लिकेशन में मुफ़्त जैसा मालूम होता है। परफ़ॉर्मेंस की वही सलाह जो कोड बदलती है, शैप के बारे में है। आउटपुट एक C# स्ट्रिंग है, और C# स्ट्रिंग हर चर को दो बाइट्स में स्टोर करती है, तो एन्कोडेड रिज़ल्ट की मेमोरी-ख़र्च लगभग हर इनपुट बाइट पर 2.7 बाइट्स है (3 इनपुट बाइट्स पर 4 चर, 2 बाइट्स प्रति चर)। एक ऐसा नंबर जो पेलोड मेगाबाइट में हो तो जानना चाहिए। अगर आप लूप में हज़ारों छोटे पेलोड्स एन्कोड कर रहे हैं, तो स्ट्रिंग API की बजाय स्पैन और चर-बफ़र API को पसंद करें, जो उन बफ़र्स में लिखते हैं जो आप दोबारा इस्तेमाल करते हैं, स्ट्रिंग API हर कॉल पर एक नई मैनेज्ड स्ट्रिंग अलोकेट करती है। अगर एक बड़ी फ़ाइल एन्कोड कर रहे हैं, तो स्ट्रिंग को ही छोड़ दें और स्ट्रीमिंग ट्रांसफ़ॉर्म इस्तेमाल करें, क्योंकि 13 मेगाबाइट की स्ट्रिंग पकड़े रहने की 2-बाइट्स-प्रति-चर की ख़र्च सरासर बेकार है जब CopyTo वर्किंग सेट को स्ट्रीम बफ़र्स में ही रख लेता है। और अगर MIME-रैप्ड आउटपुट बना रहे हैं, तो याद रखें कि रैपिंग वाली पारी डेटा पर दूसरा सफ़र है, तो सिर्फ़ तब रैप करें जब ट्रांसपोर्ट को चाहिए, डिफ़ॉल्ट के तौर पर नहीं।

सुरक्षा पर बात

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

दूसरी सीख चैनल के बारे में है, और यह ख़ास तौर पर इस लेख की बनाई चीज़ों तक पहुँचती है। Basic auth हेडर पासवर्ड को ऐसे रूप में ढोता है जो कोई भी प्रॉक्सी, कोई भी लॉग, कोई भी मिडलबॉक्स पढ़ सकता है, इसलिए स्कीम सिर्फ़ TLS के ऊपर मंज़ू है और लीगेसी इंटीग्रेशन के बाहर ज़्यादातर आउटडेटेड है। HTML में डेटा URI इमेज ढोता है, और अगर इमेज यूज़र से मिली SVG है, तो वह जो कुछ SVG ढोता है वही ढोता है, इसलिए SVG-in-data-URI के केस को किसी भी यूज़र कंटेंट जितनी सावधानी चाहिए। और URL में Base64 वैल्यू, हूबहू, URL में है, यानी ब्राउज़र हिस्ट्री में, सर्वर access लॉग में, रीफ़रर हेडर में, प्रॉक्सी कैश में, तो प्राइवेट रहने वाले टोकन क्वेरी स्ट्रिंग में नहीं बैठते, पैडेड हों या न हों। तीनों केस में एन्कोडर अपना ईमानदार काम कर रहा है, बाइट्स को ढोने लायक स्ट्रिंग में बदलना। सुरक्षा उस चीज़ में है जो आप ढोते हैं, और जहाँ, और फ़ॉर्मेट ज़्यादातर कूरियर्स से अच्छा है, लेकिन वह कूरियर है, वॉल्ट नहीं।

वे फँसाव जिनमें C# एन्कोडर गिरते हैं

यह वे फँसाव हैं जो C# कोड की एन्कोडिंग तरफ़ बार-बार सामने आते हैं, और हर एक के पीछे फ्रेमवर्क के काम करने के ढंग में एक ख़ास कारण है:

  • वह कैरेक्टर सेट जो आपने नहीं चुना। Encoding.Default से स्ट्रिंग एन्कोड करने से .NET Framework (Windows की ANSI code page) और .NET (UTF-8) पर अलग-अलग Base64 बनता है। दोनों आउटपुट वैध हैं, दोनों अपने-अपने होम प्लेटफ़ॉर्म पर "सही" डिकोड होते हैं, लेकिन बाइट्स एक जैसे नहीं हैं। एन्कोडिंग को स्पष्ट रूप से पिन करें।
  • दोगुनी एन्कोडिंग। इनपुट पहले से Base64 था (एक कॉन्फ़िग जिसने एन्कोडेड वैल्यू को एन्कोड किया, एक API जो अपने इनपुट को दोबारा एन्कोड करती है), और एन्कोडर, बिल्कुल वही करता हुआ जो कहा गया था, Base64 of Base64 बना दिया। रिज़ल्ट सही लगता है, और एक-एक लेयर में डिकोड होता है, यही वह तरिका है जिससे दो डिकोड में ठीक होने वाला बग प्रोडक्शन में मिलता है।
  • ग़लत जगह लाइन ब्रेक्स। MIME-रैप्ड रूप, CRLF जोड़ों के साथ, एक JSON स्ट्रिंग, JWT सेगमेंट, या URL पैरामीटर में पहुँचता है, जहाँ सख़्त कन्स्यूमर उस व्हाइटस्पेस पर अटक जाता है जिसकी उम्मीद करने का उसे कभी नहीं बताया गया। ईमेल के लिए रैप करें, बाक़ी हर जगह छूएँ नहीं, और अगर किसी और के रैपिंग को काटें, तो \n के साथ-साथ \r भी काटें।
  • URL में स्टैंडर्ड वर्णमाला। क्वेरी स्ट्रिंग में + फ़ॉर्म-पार्सिंग के नियमों के तहत स्पेस की तरह डिकोड होता है, तो URL में रखा स्टैंडर्ड Base64 वैल्यू, जहाँ प्लस चिह्न थे वहीं अक्षर लेकर लौटता है। URL-सुरक्षित वर्णमाला इस्तेमाल करें, या पूरी वैल्यू को परसेंट-एन्कोड करें, और कभी दोनों साथ में नहीं।
  • पैडिंग का फ़र्क़। आपका आउटपुट पैडेड है और कन्स्यूमर कॉम्पैक्ट चाहता है, या उल्टा, और दोनों तरफ़ कोई ग़लत नहीं बस मतभेद है। ठीक पैडिंग सेक्शन वाला हिसाब-किताब है, वह उसी तरफ़ लागू करें जो कन्स्यूमर की उम्मीद जानती है, जो आम तौर पर टोकन लिखने वाली तरफ़ होती है।
  • मेमोरी जो बजट की गई नहीं। एन्कोडेड स्ट्रिंग मेमोरी में 2 बाइट्स प्रति चर है, तो 10 MB की फ़ाइल बनती है 13 मिलियन चरों की स्ट्रिंग, जो मैनेज्ड मेमोरी में लगभग 27 MB वज़न की होती है, और ऐसे स्ट्रिंग एक-एक करके बनाने वाला लूप प्रोफ़ाइलर में अलोकेशन चर्न की तरह दिखता है, बिना किसी दिखने वाले कारण के। बफ़र्स की साइज़ लंबाई हेल्परों से लगाएँ, बड़ी चीज़ें स्ट्रीम करें, हॉट लूप्स में बफ़र्स दोबारा इस्तेमाल करें।
  • वह ट्रांसफ़ॉर्म जो रैप नहीं करता। ToBase64Transform एक लंबी पंक्ति देता है। ऐसा कोड जो "MIME-रेडी" एटैचमेंट को इसमें स्ट्रीम करके ईमेल भेजता है, 120,000 चरों की पंक्ति बनाता है, जो कुछ ट्रांसपोर्ट समूह के बीच में दोबारा रैप कर देंगे, यही वह तबाही है जिससे बचाने के लिए 76-चर वाला नियम बना था।
  • एन्कोडिंग की एन्कोडिंग। "डेटा तो पहले से टेक्स्ट है" कहकर Base64 स्ट्रिंग को एन्कोडर में देना दूसरी लेयर बना देता है। एन्कोडर को न पता है, और न सवाल है, कि उसके इनपुट Base64 जैसे लगते हैं। वह स्ट्रिंग के जितने भी चर हैं उतने एन्कोड करता है, और दूसरी तरफ़ का डिकोडर अपने डेटा की उम्मीद में एक Base64 स्ट्रिंग पाता है।

एन्कोडर कैसे बढ़ा: वर्ज़न-दर-वर्ज़न दौरा

API की एन्कोडिंग तरफ़ का अपना टाइमलाइन है, और वह दूसरी .NET रिलीज़ से शुरू होकर अभी प्रिव्यू में वाली तक चलता है:

  • .NET Framework 1.1, अप्रैल 2003। Convert.ToBase64String और ToBase64CharArray आते हैं, पूरा क्लासिक परिवार एक ही रिलीज़ में, स्लाइस ओवरलोड पहले से शामिल, जो 2003 की API के लिए एक छोटा आगे-देखने का चमत्कार है।
  • .NET 2.0, 2005। Base64FormattingOptions और InsertLineBreaks वैल्यू परिवार में जुड़ते हैं, MIME लाइन-रैपिंग को फ्रेमवर्क में लाते हैं, और ईमेल कोड में हाथ-से-बनाने वाले Substring लूप के दौर का अंत करते हैं।
  • .NET Core 2.1, 2018। Span का दौर। Convert को स्पैन-आधारित एन्कोड और TryToBase64Chars मिलते हैं, और नई System.Buffers.Text.Base64 क्लास अपने OperationStatus करार और इन-प्लेस इन्फ्लेशन के साथ आती है, ज़ीरो-अलोकेशन दुनिया के लिए बनी।
  • .NET 5, 2020। हेक्स जुड़वाँ (Convert.ToHexString और उसके साथी) शिप होते हैं, वही डिज़ाइन पैटर्न 16-चिह्न वर्णमाला पर, और कन्वर्जन-क्लास पैटर्न एक हाउस स्टाइल बन जाता है।
  • .NET 7, 2022। X509Certificate2.ExportCertificatePem फ्रेमवर्क को PEM बनाने देता है, कवच मार्कर, 64-चर रैपिंग और Base64 बॉडी समेत, जो चुपचाप सर्टिफ़िकेट-फ़ॉर्मेटिंग कोड के एक पूरे वर्ग को रिटायर कर देता है।
  • .NET 9, नवंबर 2024। System.Buffers.Text.Base64Url सालों के कम्युनिटी रिक्वेस्ट्स के बाद बॉक्स में आता है, Microsoft.Bcl.Memory पैकेज जो इसे .NET Framework 4.6.2 और ऊपर बैकपोर्ट करता है, और बिना-पैडिंग व्यवहार जो JWT कोड हाथ से बनाती आ रही थी।
  • .NET 11, इस लेख लिखते समय प्रिव्यू में। अगली रिलीज़, 2026 के अंत में उम्मीद, मौजूदा टाइप्स में और Base64 सुविधा API और ओवरलोड जोड़ती है, और ज़्यादा सुविधाजनक सरफ़स की ओर मार्च जारी रखती है।

फ़ॉर्मैट खुद की एक पुरानी बायोग्राफी है, और यही वजह है कि C# API इस तरह दिखता है। जिस चीज़ को अब MIME Base64 कहते हैं उसका पहला स्टैंडरडाइज़्ड उपयोग 1987 में Privacy-Enhanced Mail प्रोटोकॉल था (RFC 989), MIME विनिर्देश ने 1993 में 76-चर की रैप्ड पंक्ति-रूप तय किया, और RFC 4648 ने 2006 में फ़ॉर्मेट को उसकी आधुनिक, वर्णमाला-जागरूक विनिर्देश दिया, URL-सुरक्षित वेरिएंट समेत, जिसके लिए C# को 2024 तक फ़र्स्ट-क्लास एन्कोडर मिला। तीन दशकों की ईमेल और वेब की रवाइयों के कारण लाइन ब्रेक्स, पैडिंग, और दो वर्णमालाएँ सब मौजूद हैं, और C# एन्कोडर वही जगह है जहाँ तीनों मिलती हैं।

छोटे-छोटे चमत्कार

  • 4-चर का न्यूनतम। संभव न्यूनतम ग़ैर-ख़ाली Base64 आउटपुट 4 चर है, क्योंकि फ़ॉर्मेट चारों के समूहों में सोचता है, चाहे आप एक बाइट ही दें। किसी भी एक बाइट का एन्कोड 2 अक्षर और 2 = देता है, और वह रूप, 2 डेटा चर जो पैडिंग की टोपी पहने हैं, एक फिंगरप्रिंट है जो आप कॉन्फ़िग और टोकन में पहचानना शुरू कर देंगे।
  • NULs का स्वागत है। एन्कोडर को बाइट्स के मतलब पर कोई राय नहीं, तो ज़ीरो से भरा बफ़र A चरों की दीवार बनकर एन्कोड होता है, और NUL बाइट्स वाली बाइनरी फ़ाइल रउंड ट्रिप होती है एक भी न खोए। "स्ट्रिंग बाइनरी नहीं पकड़ सकती" की चिंटा टाइप सिस्टम की स्ट्रिंग तरफ़ की है, एन्कोडर की नहीं, जो कभी स्ट्रिंग नहीं देखता।
  • निश्चितता एक सुविधा के रूप में। वही बाइट्स, वही ऑप्शन, हमेशा वही स्ट्रिंग। न कोई टाइमस्टैम्प, न कोई रैंडम सॉल्ट, न कोई वेरिएशन। इसीलिए Base64 स्ट्रिंग फ़ाइल के कंटेंट की एक काम आने वाली क्विक-एंड-डर्टी फिंगरप्रिंट बनती है: Base64 जिन दो फ़ाइलों में एक जैसे हैं वे वही फ़ाइल हैं, और जाँच सिर्फ़ स्ट्रिंग तुलना है।
  • 2 बाइट्स प्रति चर, मुफ़्त में। C# स्ट्रिंग UTF-16 होती है, तो आपके Base64 आउटपुट का हर चर मैनेज्ड मेमोरी में 2 बाइट्स जगह लेता है। एन्कोडर यह घोषणा नहीं करता, लंबाई प्रॉपर्टी यह रिपोर्ट नहीं करती, और 13 मिलियन चरों की स्ट्रिंग बस 26 MB वज़न की है, यही वह नंबर है जो पेलोड बड़ा हो तो आपके सिर में होना चाहिए।
  • परंपरा से CRLF। MIME रैपिंग तब भी कैरिएज-रिटर्न-लाइन-फ़ीड जोड़े डालता है जब आपका कोड Linux पर चले, क्योंकि यह नियम मेल विनिर्देश से आया है, प्लेटफ़ॉर्म से नहीं। एन्कोडर कन्वर्टर के बराबर हिस्टोरियन भी है, जो 2026 की मशीन पर 1993 की लाइन-एंडिंग सहेजता है।
  • पहले दिन से स्लाइस ओवरलोड। ToBase64String(byte[], int, int) 2003 से बड़े ऐरे की खिड़कियाँ एन्कोड करता आ रहा है, स्पैन से उस विचार को फ़ैशनेबल बनने से पंद्रह साल पहले। 1.1-के-दौर के API डिज़ाइनर्स ने असली बफ़र्स देखकर ऑफ़सेट-एंड-लंबाई रूप जोड़ा, और जब डेटा किसी बड़े रीड का हिस्सा हो तो अभी भी सही चुनाव है।
  • 64-चर सर्टिफ़िकेट लाइन। PEM 64 चरों पर रैप होता है, 76 पर नहीं, और ExportCertificatePem यह जानता है और उसी अनुसार रैप करता है, सर्टिफ़िकेट काम के लिए "फ्रेमवर्क को करने दो" सही सलाह बनाने की चुप-चुप बारीकियों में से एक। दो रैपिंग चौड़ाई, एक फ़ॉर्मेट परिवार, और फ्रेमवर्क उन्हें सही-सलामत रखता है।
  • दो वर्णमालाएँ, दो नाम। 64 वैल्यूज़ API के एक हिस्से में "स्टैंडर्ड" कहलाती हैं और दूसरे में "URL-safe", और फ़र्क़ बिल्कुल 2 चरों का है: 62वाँ और 63वाँ स्लॉट। एक तरफ़ + और /, दूसरी तरफ़ - और _, और इस लेख के हर इंटरऑप बग वही पल में बसे हैं जब किसी ने मान लिया कि दोनों तरफ़ एक हैं।

फिर से वही जगह

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

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

संबंधित लेख: C# (CSharp) में Base64 डिकोडिंग: एक सम्पूर्ण गाइड