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

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

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

इंस्टॉल करने को कुछ नहीं है। Base64 Dart 1.13 (2015) से dart:convert के साथ-साथ आ रहा है, और API तब से स्टेबल है; दोनों वर्णमालाएँ - स्टैंडर्ड और URL-सेफ़ - दस साल से ज़्यादा पुरानी हैं। होम पेज पर फ़ॉर्मेट का गहरा दौर होता है; यहाँ काम की एन्कोडिंग-वाली तरफ़ है: पूरा API सर्फ़ेस, बाइट्स-फ़र्स्ट अनुशासन जो सबसे आम बग से बचाती है, पैडिंग और वर्णमाला के चुनाव, और असल-दुनिया के काम: JWTs, data URIs, फ़ाइल-अपलोड, HTTP हेडर्स, MIME, कॉन्फ़िग, स्ट्रीम्स और कमांड लाइन। डिकोडिंग, यानी उल्टी दिशा, का अपना गाइड है, जो अंत में लिंक है।

एक इम्पोर्ट, दो वर्णमालाएँ, एक पैडिंग नियम

एन्कोडिंग का पूरा पब्लिक सर्फ़ेस dart:convert में रहता है:

एंट्री वर्णमाला कब हाथ आँचें
base64Encode(bytes) स्टैंडर्ड: A-Z a-z 0-9 + /, पैड किया हुआ APIs, MIME, Basic auth, ज़्यादातर कन्ज़्यूमर
base64UrlEncode(bytes) URL-सेफ़: A-Z a-z 0-9 - _, फिर भी पैड किया हुआ URLs, फ़ाइल-नेम, JWTs, ऑब्जेक्ट आईडी
base64.encode(bytes) स्टैंडर्ड, टॉप-लेवल कॉल से बिल्कुल एक जैसा स्ट्रीम ट्रांसफ़ॉर्मेशन और कोडेक पिपलाइन्स
Base64Encoder().convert(bytes) स्टैंडर्ड आप एक नामित एन्कोडर इंस्टेंस चाहते हैं

दो नियम चारों पंक्तियों को कवर करते हैं। पहला, इनपुट बाइट वैल्यूज़ की लिस्ट होनी चाहिए, 0 से 255 तक के इंटीजर्स; बाकी कुछ भी - नकारात्मक नंबर या 256 और ऊपर - ArgumentError फेंकता है, जो ख़राब इंडेक्स का नाम बताता है। दूसरा, आउटपुट हमेशा पैड होता है: न तो कोई फ्लैग है, न कॉन्स्ट्रक्टर, न कोई विकल्प जो बिना-पैड आउटपुट दे; क्योंकि फ़ॉर्मेट की पैडिंग डेटा की एक प्रॉपर्टी है, और वही स्पेक्स जो इसे हटाना चाहती हैं, उसे एक अलग, डॉक्यूमेंट किया हुआ स्टेप के तौर पर ही हटाती हैं। सबसे छोटा सम्भव उदाहरण, शुरू से अंत तक:

import 'dart:convert';
void main() {
  final text = 'Dart is open source';
  final bytes = utf8.encode(text);
  final encoded = base64Encode(bytes);
  print(encoded); // RGFydCBpcyBvcGVuIHNvdXJjZQ==
}

बाइट्स पहले: वह क्रम जो आपको बचाता है

सबसे आम Dart base64 बग base64 से जुड़ा ही नहीं है। यह ऑपरेशन्स के क्रम का बग है। Dart String UTF-16 कोड यूनिट्स की सिलसिला है, और base64Encode(text.codeUnits) कॉल करने से उन 16-बिट यूनिट्स को पैक किया जाता है - वो बाइट्स नहीं जो रिसीवर की उम्मीद है। शुद्ध ASCII के लिए दोनों बस-बस मेल खाते हैं, इसलिए यह बग छुपा रहता है तब तक कि पहला एक्सेंटेड चर, इमोजी, या CJK टेक्स्ट न आ जाए। फिर एन्कोडर काम मना लेता है, क्योंकि 0x4e16 जैसा कोड यूनिट कोई बाइट वैल्यू नहीं है:

import 'dart:convert';
void main() {
  final message = 'Héllo Wörld 世界';
  print(utf8.encode(message).length); // 20
  print(message.codeUnits.length); // 14
  print(base64Encode(utf8.encode(message)));
  try {
    base64Encode(message.codeUnits);
  } on ArgumentError catch (e) {
    print(e);
  }
}

ArgumentError बिल्कुल उसी ख़राब इंडेक्स की तरफ़ इशारा करता है, इसलिए फेलियर ख़ामोश नहीं, दहाड़ती है। वह नज़दीकी जो रखनी है: base64 से बात करने से पहले तय कर लो कि बाइट्स क्या हैं। टेक्स्ट एक नामित एन्कोडिंग से गुज़रता है - मॉडर्न डेटा के लिए utf8.encode - और जो List<int> बनता है, वह ही पैक होता है। फ़ाइल या नेटवर्क सोकेट से आने वाले बाइट्स पहले से Uint8List की शक्ल में आते हैं, जो कि एन्कोडर के लिए बिना किसी कन्वर्ज़न की सही शक्ल है।

पैडिंग: एन्कोडर की ज़िम्मेदारी

Base64 तीन बाइट्स के ग्रुप को चार चरों पर मैप करता है, इसलिए जिस पेलोड की लंबाई तीन का गुणज न हो, उसके अंत में एक अधूरा ग्रुप रह जाता है। फ़ॉर्मेट उस घाट को = चरों से चिह्नित करता है: एक इनपुट बाइट चार चर + दो पैड बनती है, दो बाइट्स चार चर + एक पैड, तीन बाइट्स बिल्कुल चार चर बनती हैं। Dart एन्कोडर यह काम आपके लिए, बिना शर्त, कर देता है:

import 'dart:convert';
void main() {
  print(base64Encode([0x41])); // QQ==
  print(base64Encode([0x41, 0x42])); // QUI=
  print(base64Encode([0x41, 0x42, 0x43])); // QUJD
}

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

base64UrlEncode(bytes).replaceAll('=', '')

स्ट्रिप वहीँ रखो जहाँ स्पेक की बाउंडरी है, इसे नाम दो, और इसे डॉक्यूमेंट करो। इस डील का डिकोडर-वाला हिस्सा - जिसमें ख़राब या कटी हुई इनपुट का रिपेयर कैसे होता है, वह भी - डिकोडिंग गाइड में कवर है।

URL-सेफ़ Base64

स्टैंडर्ड वर्णमाला में +, / और = हैं, और ये तीनों चर URL सिंटैक्स से टकराते हैं: क्वेरी सैपरेटर्स, पाथ सैपरेटर्स, और पैरामीटर डिलीमिटर। URL-सेफ़ वर्णमाला, जो RFC 4648 में base64url के नाम से स्टैंडर्ड है, + की जगह - और / की जगह _ लगाती है, ताकि आउटपुट बिना एस्केपिंग के पाथ सेगमेंट, क्वेरी वैल्यू, या फ़ाइल-नेम में बैठ सके। दोनों बदले गए चरों को परखने वाले बाइट्स पर फ़र्क़ यहाँ है:

import 'dart:convert';
void main() {
  final tricky = [0xfb, 0xff, 0xfe, 0xf9];
  print(base64Encode(tricky)); // +//++Q==
  print(base64UrlEncode(tricky)); // -__--Q==
}

कन्ज़्यूमर के हिसाब से चुनो, स्वाद के हिसाब से नहीं। अगर वैल्यू URL, JWT, या फ़ाइल-नेम में रहेगी, तो base64UrlEncode से एन्कोड करो और अगर स्पेक बिना-पैड है तो पैडिंग हटा दो। अगर वैल्यू MIME बॉडी, Basic auth हेडर, या ऐसी API कॉन्ट्रैक्ट की फ़ील्ड होगी जो "base64" कहती है, तो स्टैंडर्ड वर्णमाला इस्तेमाल करो, क्योंकि क्वालिफायर के बिना base64 मतलब स्टैंडर्ड वाली का है। सख़्त कन्ज़्यूमरों की नज़र में दोनों वर्णमालाएँ एक-दूसरे की जगह नहीं ले सकतीं: स्टैंडर्ड base64 उम्मीद करने वाला सर्वर - वाले पेलोड को 400 के साथ रिजेक्ट कर सकता है - और कुछ ज़्यादा मददगार नहीं।

चरसेट: आप कौन-से बाइट्स पैक कर रहे हैं?

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

एन्कोडिंग इसका इस्तेमाल इसी के लिए किससे एन्कोड करें
utf8 मॉडर्न टेक्स्ट, JSON, वेब पर कुछ भी utf8.encode(text)
latin1 लेगासी पश्चिमी सिंगल-बाइट डेटा latin1.encode(text)
ascii सादा 7-बिट टेक्स्ट ascii.encode(text)
import 'dart:convert';
void main() {
  final modern = base64Encode(utf8.encode('Héllo'));
  final legacy = base64Encode(latin1.encode('Héllo'));
  print(modern); // SMOpbGxv
  print(legacy); // SOlsbG8=
}

वही शब्द, अलग बाइट्स, अलग base64। लंबाई पर नज़र रखो: UTF-8 को Héllo के लिए छह बाइट्स चाहिए क्योंकि एक्सेंट एक दो-बाइट सिक्वेंस है, जबकि Latin-1 इसे पाँच में सजा देता है। अगर कन्ज़्यूमर उस एन्कोडिंग से डिकोड करे जो आपने इस्तेमाल नहीं की, तो उसे मोजिबेक मिलेगा - और ऐसा लगेगा कि डेटा रास्ते में ख़राब हुआ, जबकि असल में वह इरादे में ही ख़राब हुआ था।

JWTs: टोकन लिखना

JSON Web Token तीन base64url हिस्सों से बनता है जो डॉट्स से जुड़े होते हैं: हेडर, पेलोड, सिग्नेचर। RFC 7515 दो डिटेल फिक्स करता है: वर्णमाला URL-सेफ़ है, और पैडिंग छोड़ दी गई है, क्योंकि टोकन की डिज़ाइन URL और हेडर्स में बैठने के लिए है। HS256 एल्गोरिदम के लिए सिग्नेचर header.payload का HMAC-SHA256 होता है, जो खुद बिना-पैड base64url है। crypto पैकेज के साथ इसे हाथ से बनाना कुछ लाइनों का काम है, और यह दिखने से ज़्यादा ट्रांसपेरेंट है:

import 'dart:convert';
import 'package:crypto/crypto.dart';
String base64UrlNoPadding(List<int> bytes) {
  return base64UrlEncode(bytes).replaceAll('=', '');
}
String createJwt(Map<String, dynamic> header, Map<String, dynamic> payload,
    List<int> secretKey) {
  final signingInput =
      '${base64UrlNoPadding(utf8.encode(jsonEncode(header)))}.'
      '${base64UrlNoPadding(utf8.encode(jsonEncode(payload)))}';
  final mac = Hmac(sha256, secretKey).convert(utf8.encode(signingInput));
  final signature = base64UrlNoPadding(mac.bytes);
  return '$signingInput.$signature';
}
void main() {
  final token = createJwt(
    {'alg': 'HS256', 'typ': 'JWT'},
    {'sub': 'user-42', 'exp': 1893456000},
    utf8.encode('a-32-byte-secret-key-0123456789'),
  );
  print(token);
}

सिग्नेचर वही बाइट्स पर गिना जाता है जो पैक किए गए थे, इसलिए जब तक आप वही स्ट्रिंग साइन करते हैं जिसे आप बाहर भेजते हैं, दूसरी तरफ़ वेरिफिकेशन वही स्टेप्स दोहराना है। तीन चेतावनियाँ। pub.dev का पुराना jwt पैकेज 2014 का है और null safety से पहले का; एकोसिस्टम का काम करने वाला जवाब यहीं दिखाई गई चीज़ को crypto के साथ करना है। कभी भी alg: none वाला टोकन मत बनाओ, और कभी भी क्लाइंट को एल्गोरिदम चुनने मत दो। और याद रखो कि पेलोड कोई भी पढ़ सकता है, इसलिए टोकन में सिर्फ़ उतना ही डालो जितना वह प्रूव करने के लिए बना है।

Data URIs: टेक्स्ट के अंदर फ़ाइलें भेजना

data URI, जिसे RFC 2397 में तय किया गया है, एक URL है जिसका पेलोड खुद डेटा है। data URI के अंदर का बायनेरी कंटेंट base64 एन्कोड होता है, इसलिए यह फ़ॉर्मेट हर उस जगह दिखता है जहाँ टेक्स्ट डॉक्यूमेंट्स को इमेजेस, फॉन्ट्स, या अटैचमेंट्स इम्बेड करने हों: HTML एट्रिब्यूट, CSS, JSON, कॉन्फ़िग फ़ाइलें। Dart इन URIs को खुद-ब-खुद बना सकता है, कोई URI पैकेज ज़रूरी नहीं:

import 'dart:convert';
import 'dart:io';
Future<void> main() async {
  final png = await File('icon.png').readAsBytes();
  final imageUri = Uri.dataFromBytes(png, mimeType: 'image/png');
  print(imageUri); // data:image/png;base64,iVBOR...
  final note = Uri.dataFromString('Hello, Dart!');
  print(note); // data:,Hello,%20Dart!
}

Uri.dataFromBytes डिफ़ॉल्ट रूप से base64 एन्कोड करता है (दूसरी शक्ल के लिए percentEncoded: true ऑप्ट-इन है), जो बायनेरी के लिए सही एन्कोडिंग है। Uri.dataFromString डिफ़ॉल्ट रूप से परसेंट एन्कोड करता है, क्योंकि छोटा टेक्स्ट उस तरह छोटा रहता है, और जब आप बाइट्स-पैक्ड शक्ल चाहते हो तो base64: true फ्लैग लेता है। असली गड्ढा स्केल है: पेलोड 33 फ़ीसद ओवरहेड के साथ वहीँ डॉक्यूमेंट के अंदर सवार रहता है, इसलिए data URIs छोटे एसेट्स, आइकॉन्स और थंबनेल्स के लिए हैं - CSS के रास्ते मेगाबाइट्स भेजने के लिए नहीं।

फ़ाइलें: टेक्स्ट चैनल्स के लिए बाइट्स पैक करना

रोज़ का काम: फ़ाइल जो JSON से, कॉन्फ़िग फ़ाइल से, या किसी भी सिर्फ़-टेक्स्ट ट्रांसपोर्ट से गुज़रनी चाहिए। पैटर्न है - बाइट्स पढ़ो, एन्कोड करो, इम्बेड करो:

import 'dart:convert';
import 'dart:io';
Future<void> main() async {
  final image = await File('photo.jpg').readAsBytes();
  final encoded = base64Encode(image);
  final upload = jsonEncode({
    'name': 'photo.jpg',
    'size': image.length,
    'data': encoded,
  });
  print('payload ${upload.length} chars for ${image.length} bytes');
}

जो नंबर दिमाग़ में रखना है वह ग्रोथ है: 2,000-बाइट फ़ाइल 2,668 base64 चर बन जाती है, और JSON कीज़ जुड़ने पर थोड़ा और। दो गड्ढे। पहला, जाँचो कि आपका इनपुट पहले से एन्कोड तो नहीं है: पहले-से-base64 स्ट्रिंग को base64 एन्कोड करना क्लासिक डबल-एन्कोड बग है, और यह "सफलता" से एक और base64 की दीवार में डिकोड हो जाता है। दूसरा, अगर चैनल बायनेरी उठा सकता है - यही तो multipart/form-data के लिए बना है - तो बायनेरी ही उठाओ: वह चौथाई छोटा होता है, और base64 टैक्स बर्बाद की बर्बाद है।

HTTP और APIs: हेडर्स और पेलोड्स

HTTP में सबसे पहचान वाली एन्कोडिंग-वाली जॉब Authorization: Basic हेडर है: शब्द Basic, एक स्पेस, और username:password का स्टैंडर्ड-वर्णमाला base64:

import 'dart:convert';
import 'package:http/http.dart' as http;
Future<void> main() async {
  final credentials = base64Encode(utf8.encode('octocat:secret'));
  final client = http.Client();
  final response = await client.get(
    Uri.parse('https://httpbin.org/basic-auth/octocat/secret'),
    headers: {'Authorization': 'Basic $credentials'},
  );
  print(response.statusCode);
  client.close();
}

http पैकेज के साथ - बस एक dart pub add http दूर - हेडर रिक्वेस्ट में बस एक स्ट्रिंग है। गड्ढा सिक्योरिटी-फ्रेमिंग में है: यहाँ base64 छुपाई है, सुरक्षा नहीं। कोई भी इसे एक स्टेप में उल्टा कर सकता है, और यही वज़ह है कि Basic auth सिर्फ़ TLS कनेक्शन्स पर ही जगह रखता है, जहाँ सुरक्षा ट्रांसपोर्ट करता है, encoding नहीं। API पेलोड फ़ील्ड्स के लिए कॉन्ट्रैक्ट का पालन करो: अगर वह base64 कहता है, तो मतलब स्टैंडर्ड वर्णमाला पैडिंग के साथ - और URL-सेफ़ वैरिएंट एक अलग चीज़ है जो सख़्त कन्ज़्यूमर रिजेक्ट कर देंगे।

ईमेल और MIME: 76 पर रैपिंग

MIME, वह सिस्टम जो ईमेल को बायनेरी उठाने देता है, base64 को कंटेंट ट्रांसफर एन्कोडिंग के तौर पर इस्तेमाल करता है, और RFC 2045 तय करता है कि एन्कोड लाइन्स 76 चरों से ज़्यादा लंबी न हों, और उनके बीच CRLF हो। यह लिमिट MIME की कॉन्वेंशन है - 76 और CRLF एक 80-कॉलम डिस्प्ले पर आराम से बैठ जाते हैं - और हर कन्फॉर्मिंग एन्कोडर रैप करता है। Dart का एन्कोडर एक अखंड स्ट्रिंग देता है, इसलिए रैपिंग एक छोटा पोस्ट-प्रोसेसिंग स्टेप है:

import 'dart:convert';
String wrapForMime(String base64Text, [int lineLength = 76]) {
  final buffer = StringBuffer();
  for (var i = 0; i < base64Text.length; i += lineLength) {
    final end = i + lineLength > base64Text.length
        ? base64Text.length
        : i + lineLength;
    buffer
      ..write(base64Text.substring(i, end))
      ..write('\r\n');
  }
  return buffer.toString();
}
void main() {
  final encoded = base64Encode(utf8.encode('Hello from an email attachment'));
  print(wrapForMime(encoded));
}

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

कॉन्फ़िग्यूरेशन: सीक्रेट्स को एक-लाइनर बनाना

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

import 'dart:convert';
String forEnvFile(String secret) {
  return base64Encode(utf8.encode(secret));
}
void main() {
  final line = 'API_TOKEN_B64=${forEnvFile('sk-live-abc123')}';
  print(line); // API_TOKEN_B64=c2stbGl2ZS1hYmMxMjM=
}

वैल्यू फिर .env फ़ाइल, CI सीक्रेट, या कंपाइल-टाइम डिफाइन में बैठ जाती है, और एक डिकोड के बाद सादे टेक्स्ट के रूप में वापस आ जाती है। अगर सीक्रेट को रास्ते में या जमकर रखे जाने पर प्रोटेक्ट करना हो, तो सीक्रेट मैनेजर या एन्क्रिप्शन लाइब्रेरी की तरफ़ मुड़ो; base64 की जॉब यहाँ सिर्फ़ पिपलाइन के टेक्स्ट-हैंडलिंग को सादा रखना है - कुछ और नहीं।

स्ट्रीम्स: चंक बाउंडरीज़ के पार एन्कोडिंग

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

import 'dart:convert';
import 'dart:typed_data';
Future<void> main() async {
  final data = Uint8List(100000);
  for (var i = 0; i < data.length; i += 31) {
    data[i] = i % 256;
  }
  final chunks = <List<int>>[data.sublist(0, 777), data.sublist(777)];
  final encoded = await Stream.fromIterable(chunks)
      .transform(base64.encoder)
      .join();
  print('in: ${data.length}, out: ${encoded.length}'); // in: 100000, out: 133336
}

दो अजीब साइज़ के चंक्स - 777 और 99,223 बाइट्स - एक सही 133,336-चर स्ट्रिंग देते हैं, क्योंकि एन्कोडर हर अधूरे ग्रुप के बचे बिट्स को अगले चंक आने तक रोककर रखता है, और पैडिंग सिर्फ़ अंत में निकालता है। अगर आप सिंक्स पसंद करते हो, तो base64.encoder.startChunkedConversion वही स्टेट मशीन ByteConversionSink की शक्ल में देता है (आप बाइट चंक्स डालते हो, वह स्ट्रिंग्स निकालता है), जो बड़े आउटपुट को फ़ाइल या सोकेट में लिखने के लिए - कभी भी एक बड़ी स्ट्रिंग जोड़े बिना - नैटुरल फिट है।

बड़ा डेटा: थ्रूपुट और मेमोरी

साइज़ का गणित सटीक है और दिमाग़ में रखने लायक़: आउटपुट लंबाई = इनपुट लंबाई ÷ 3, ऊपर तक राउंड, फिर × 4। एक, दो, या तीन बाइट्स - तीनों की कीमत चार चर; उसके बाद फ्लैट 33 फ़ीसद ओवरहेड। फ़ॉर्मूला, तब के लिए जब बफ़رز रिज़र्व करने हों या प्रोग्रेस रिपोर्ट करनी हो:

import 'dart:convert';
int encodedLength(int n) => (n + 2) ~/ 3 * 4;
void main() {
  print(encodedLength(100000)); // 133336
}

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

कमांड-लाइन एन्कोडर

VM एन्कोडर से एक साफ़ CLI बना देता है। यह टूल एक फ़ाइल आर्गुमेंट या स्टैंडर्ड इनपुट पढ़ता है और स्टैंडर्ड-वर्णमाला एन्कोडिंग प्रिंट करता है:

import 'dart:convert';
import 'dart:io';
Future<void> main(List<String> args) async {
  final bytes = await _read(args);
  stdout.writeln(base64Encode(bytes));
}
Future<List<int>> _read(List<String> args) async {
  if (args.isNotEmpty) {
    return File(args[0]).readAsBytes();
  }
  final all = <int>[];
  await for (final chunk in stdin) {
    all.addAll(chunk);
  }
  return all;
}

इसे bin/encode.dart के नाम से सहेजो और dart run bin/encode.dart photo.jpg > photo.b64 चलाओ, या cat config | dart run bin/encode.dart से पाइप करो। साथी - एक डिकोडर जो पढ़ता और सपाट करता है - डिकोडिंग गाइड का पहला उदाहरण है, और दोनों मिलकर बायनेरी को टेक्स्ट चैनल्स से गुज़ारने का छोटा पर सच में काम का टूलसेट हैं।

बाहर निकलने के रास्ते में ऐसे गड्ढे जो काटते हैं

  • codeUnits ट्रैप। base64Encode(text.codeUnits) UTF-16 यूनिट्स पैक करता है, बाइट्स नहीं; यह ASCII के लिए चलता है और 255 से ऊपर पहले कोड यूनिट पर ArgumentError फेंकता है। टेक्स्ट को हमेशा पहले एक नामित एन्कोडिंग से एन्कोड करो।
  • वर्णमाला की ग़लती। URL-सेफ़ आउटपुट को उस कन्ज़्यूमर को देना जो स्टैंडर्ड वर्णमाला उम्मीद करता है, एक 400 है जो बस होने का इंतज़ार कर रही है। वर्णमाला स्पेक से तय करो, एक बार एन्कोड करो, और बाद में कन्वर्ट मत करो।
  • पैडिंग की धारणाएँ। Dart हमेशा पैड करता है। अगर स्पेक बिना-पैड चाहता है, तो बाउंडरी पर एक एक्सप्लिसिट स्टेप के तौर पर replaceAll('=', '') से हटाओ, और कॉन्ट्रैक्ट में यह भी कह दो।
  • चरसेट का झुकाव। उस कन्ज़्यूमर के लिए Latin-1 बाइट्स एन्कोड करना जो UTF-8 डिकोड करता है, ग़लत डेटा का वैध base64 देता है। कुछ भी थ्रॉ नहीं होता; टेक्स्ट बस ग़लत होता है।
  • डबल एन्कोडिंग। पहले से base64 वैल्यू को base64 एन्कोड करना - कोई और कॉन्फ़िग से कॉपी किया गया टोकन - वही क्लासिक "डिकोड करने पर एक और base64 की दीवार" बग है।
  • गोपनीयता की भ्रांति। Base64 एक फ़ॉर्मेट है, साइफर नहीं। अगर थ्रेट मॉडल में एक पाठक शामिल है, तो जवाब एन्क्रिप्शन है, एन्कोडिंग नहीं।
  • पुराने पैकेज। pub.dev का लंबे समय से चलता jwt पैकेज null safety से पहले का है; JWT के काम के लिए, crypto और ऊपर की वह कुछ लाइन्स ही मेंटेन किया जाने वाला रास्ता है।

कब किसी और की तरफ़ मुड़ें

  • HTTP पर फ़ाइल-अपलोड। multipart/form-data इस्तेमाल करो; वह राव बाइट्स उठाता है, इसलिए आप 33 फ़ीसद टैक्स से बिल्कुल बच जाते हो।
  • बड़े या बार-बार आने वाले पेलोड। पहले कंप्रेस करो, फिर एन्कोड: gzipped टेक्स्ट का base64, वही टेक्स्ट के base64 से बेहद छोटा होता है, और डीकंप्रेस करने वाला पक्ष फ़ॉर्मेट पहले से जानता है।
  • URL के अंदर छोटा टेक्स्ट। कुछ चरों के लिए परसेंट एन्कोडिंग छोटी रहती है और वैल्यू को इंसान-पढ़ने लायक़ रखती है; data URIs डिफ़ॉल्ट में खुद ही यह कर देते हैं।
  • डिबग आउटपुट और लॉग्स। Hex, base64 से 50 फ़ीसद लंबा है (राव साइज़ का दोगुना, base64 सिर्फ़ चार-तीहाई) पर पढ़ने, डिफ करने, और किसी साथी को सौंपने में ज़्यादा आसान; लॉग्स में बायनेरी स्निपेट्स के लिए यह आमतौर पर जीतता है।

बेस्ट प्रैक्टिस: एन्कोडर की लिस्ट

  • बाइट्स एन्कोड करो, कभी कोड यूनिट्स नहीं; टेक्स्ट पहले नामित एन्कोडिंग से गुज़रे।
  • वर्णमाला कॉल लिखने से पहले कन्ज़्यूमर की स्पेक से चुनो।
  • पैडिंग सिर्फ़ वहीँ हटाओ जहाँ स्पेक बिना-पैड कहता है, बाउंडरी पर एक नज़र आने वाले स्टेप के तौर पर।
  • चरसेट को कॉन्ट्रैक्ट में एक्सप्लिसिट रूप से बताओ; दूसरे पक्ष के बारे में कुछ मत मानो।
  • जो भी बड़ा हो सकता है, उसे स्ट्रीम करो।
  • base64 को सिर्फ़-टेक्स्ट चैनल्स का फ़ॉर्मेट समझो, संवेदनशील डेटा की सुरक्षा के लिए कभी नहीं।

दो वर्णमालाओं का छोटा-सा इतिहास

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

  • 1993, RFC 1521: MIME base64 को ईमेल के लिए कंटेंट ट्रांसफर एन्कोडिंग के तौर पर पेश करता है - स्टैंडर्ड 64-चर वर्णमाला और 76-चर लाइन-लिमिट के साथ, जिस पर यही आर्टिकल रैप करती है। फ़ॉर्मेट की जॉब - बायनेरी को टेक्स्ट चैनल्स से उठाना - इसी दिन की है।
  • 1996, RFC 2045: वह RFC जिसने RFC 1521 को ओब्सॉलेट किया और base64 की पैडिंग और लाइन-लंबाई के नियमों को स्थायी स्टैंडर्ड बनाया।
  • 2006, RFC 4648: एन्कोडिंग को MIME से अलग करके खुद के नामे पर स्टैंडर्ड किया जाता है, जिसमें URL-सेफ़ वर्णमाला और यह सलाह जुड़ती है कि डिकोडर इनवालिड इनपुट रिजेक्ट करें। Dart में मिलने वाला दो-वर्णमाला वाला चुनाव इसी डॉक्यूमेंट का नतीजा है।
  • 2015, RFC 7515: JSON Web Signatures बिना-पैड base64url तय करते हैं - हर JWT के पीछे वाली कॉन्वेंशन।
  • नवंबर 2015, Dart 1.13: base64 dart:convert में आता है; अगले साल के बसंत में Dart 1.16 में URL-सेफ़ वैरिएंट आता है, और ऊपर इस्तेमाल की गई टॉप-लेवल base64Encode और base64UrlEncode कॉल्स Dart 2.0 (2018) में उतरती हैं।
  • आज, Dart 3.13: दोनों वर्णमालाएँ, हमेशा पैड की हुई, एक इम्पोर्ट दूर - 2015 से वही सख़्त और सादा मशीन।

33 फ़ीसद ओवरहेड भी 1993 से बदला नहीं है। यह गणित की प्रॉपर्टी है - तीन बाइट्स के लिए चार सिंबल्स - और हर इम्प्लीमेंटेशन, हर भाषा में, जो आप कभी भी इस्तेमाल करेंगे, यह वही भुगतती है।

एन्कोडिंग बेंच से कुछ मज़ेदार तथ्य

  • एन्कोडर बंद नहीं किया जा सकता: SDK में बिना-पैड आउटपुट के लिए कोई फ्लैग नहीं है - इसलिए "पैड हटाओ" हमेशा आपका कोड है, आपकी बाउंडरी पर, सीधी नज़र में।
  • एक बाइट चार चर बनती है: base64Encode([65]) यानी QQ==। सबसे छोटा सम्भव base64 स्ट्रिंग चार चर लंबा है, और उनमें से सिर्फ़ पहले दो इनफ़ॉर्मेशन रखते हैं; आख़िरी दो पैडिंग हैं।
  • दोनों Dart एन्कोडर पैड करते हैं, base64UrlEncode समेत। base64url में "बिना-पैडिंग" RFC 7515 की कन्ज़्यूमर कॉन्वेंशन है, वर्णमाला की प्रॉपर्टी नहीं।
  • स्टैंडर्ड वर्णमाला 7-बिट प्रिंटबल बनने के लिए डिज़ाइन की गई, और तब से डिफ़ॉल्ट बनी रही; यह कि + और / को आख़िरकार URL-सेफ़ रिप्लेसमेंट मिले, उसकी कमज़ोरी नहीं, उसके कितना केन्द्रीय बनने की निशानी है।
  • Héllo Wörld 世界 के वही 20 UTF-8 बाइट्स SMOpbGxvIFfDtnJsZCDkuJbnlYw= में पैक हो जाते हैं, जबकि स्ट्रिंग के 14 कोड यूनिट्स एन्कोडर को इंडेक्स 12 पर क्रैश कर देते। वही चर, दो बिल्कुल अलग आउटपुट, जिनमें से एक एरर है।
  • PEM फ़ाइलें - हर TLS सर्टिफिकेट के -----BEGIN CERTIFICATE----- ब्लॉक्स - हेडर्स के साथ 64 चरों पर base64 रैप की हुई होती हैं, और यह फ़ॉर्मेट 1987 का है, MIME के ईमेल के लिए base64 प्रकाशित करने से छह साल पहले का।

अब आपके पास पूरी एन्कोडिंग-वाली तरफ़ है: API सर्फ़ेस, बाइट्स-फ़र्स्ट अनुशासन, पैडिंग और वर्णमाला के फैसले, और JWTs, data URIs, फ़ाइलें, HTTP, MIME, कॉन्फ़िग, स्ट्रीम्स और शेल के काम के पैटर्न। उल्टी दिशा - इनमें से किसी स्ट्रिंग को खोलना, डिकोडर की पूरी सख़्ती, उसकी पर्सेंट-एस्केप सरप्राइज़, और उसके रिपेयर-टूल्स के साथ - Base64 डिकोडिंग गाइड में कवर है, जो ठीक नीचे लिंक है।

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

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