Python में Base64 एन्कोडिंग: एक सम्पूर्ण गाइड
यह सिक्के की दूसरी तरफ़ है। आपके हाथ में डेटा है - एक फ़ाइल, एक पासवर्ड जोड़ी, एक बाइनरी ब्लाब, Unicode का एक पैराग्राफ़ - और कहीं नीचे की तरफ़ उसे ऐसे चैनल से गुज़रना है जो सिर्फ़ अक्षरों को स्वीकार करता है। यही Base64 का पूरा काम है: डेटा के तीन बाइट्स को 64-चर वर्णमाला से चार चरों के रूप में फिर से लिखना, आख़िरी समूह को = से पूरा करके सब कुछ चारों-चारों में निकलना, और अक्षर सौंप देना। इस साइट का होम पेज वह फ़ॉर्मेट पूरा समझाता है, वर्णमाला समेत, इसलिए हम उसे एक साँस में छोड़कर बाकी वक़्त उसी पर लगाएँगे जो Python असल में उसके साथ करता है।
शुरू करने से पहले उस अर्थव्यवस्था को एक ईमानदार वाक्य का हक़ है, क्योंकि यह नंबर हर बातचीत में आ ही जाता है: उस टेक्स्ट-सुरक्षा की कीमत साइज़ है। Base64 आपके डेटा को करीब एक-तिहाई फैला देता है, हर तीन इनपुट बाइट्स के बदले चार चर, इसलिए बाइनरी का एक मेगाबाइट अक्षरों का एक मेगाबाइट और तिहाई बन जाता है। टोकन या कॉन्फ़िग वैल्यू के लिए यह कोई बात नहीं; वीडियो फ़ाइल के लिए यही वह वजह है कि अपने विकल्पों पर गंभीरता से सोचना पड़ेगा।
और अच्छी ख़बर: Python का इस पूरे का जवाब एक import और एक फ़ंक्शन है। base64.b64encode दशकों से स्टैंडर्ड लाइब्रेरी में है, इंस्टॉलेशन की ज़रूरत नहीं, और अंदर से C-speed पर चलता है। इस गाइड का बाकी हिस्सा वह लंबी पूँछ है जो इस एक-लाइन को असल दुनिया में उपयोगी बनाती है: bytes-only नियम जो सारे TypeError बगों का आधा हिस्सा रोकता है, URL-safe वर्णमाला, MIME लाइन-लपेटने के औजार, और वे प्रोटोकॉल - JWTs, HTTP हेडर, WebSocket हैंडशेक, ईमेल, PEM, data URL - जिनके अंदर Base64 ख़ामोशी से अपना काम कर रहा है।
मिलिए b64encode से
अनुबंध चार वाक्यों में समा जाता है। एक: इनपुट बाइट्स जैसी ऑब्जेक्ट है - bytes, bytearray, memoryview - और सादा स्ट्रिंग रिजेक्ट हो जाता है। दो: आउटपुट bytes ऑब्जेक्ट है, कभी str नहीं। तीन: आउटपुट हमेशा चार चरों के गुणज तक पैड किया जाता है, इसलिए सिर्फ़ एक इनपुट बाइट भी Zg== बनाता है। चार: आउटपुट एक ही लाइन है, कभी लपटा नहीं, इनपुट कितना भी बड़ा हो। इस लेख में बाकी सब कुछ उन चार वाक्यों पर टिप्पणी है:
import base64
encoded = base64.b64encode(b"foobar")
print(encoded)
# b'Zm9vYmFy'
print(len(encoded))
# 8
असली स्ट्रिंग पाने के लिए, URL या हेडर या JSON फ़ील्ड के लिए, नतीजे को ASCII के तौर पर डिकोड करें। वर्णमाला गारंटी देती है कि इसमें और कुछ नहीं हो सकता, जिससे यह स्टेप सुरक्षित और सस्ता बन जाता है:
import base64
text = base64.b64encode(b"foobar").decode("ascii")
print(text)
# Zm9vYmFy
फ़ंक्शन के सिग्नेचर पर एक आर्गुमेंट और है, altchars, और यह स्टैंडर्ड वर्णमाला के + और / को किसी दूसरी जोड़ी चरों से बदल देता है। यही बिल्कुल वह नॉब है जो URL-safe वैरिएंट के पीछे है, तो उस बात को थाम लेना - कुछ खंड आगे, टोकन और क्वेरी स्ट्रिंग की बात करते वक़्त, वह आपसे मिलेगा।
टाइप की दीवार: str, bytes नहीं है
इस लेख में पहली Python-ख़ास दीवार टाइप सिस्टम है, और इसे महसूस करना सीखने के लायक है। b64encode स्ट्रिंग को भाषा के सबसे कठोर एरर मैसेजों में से एक के साथ रिजेक्ट करता है:
import base64
try:
base64.b64encode("hello")
except TypeError as caught:
print(caught)
# a bytes-like object is required, not 'str'
फ़िक्स पूरे इस गाइड की सबसे ज़रूरी आदत है: पहले अपना टेक्स्ट बाइट्स में बदलें, और कैरेक्टर सेट उम्मीद की नहीं, जानबूझकर चुनें:
import base64
print(base64.b64encode("été".encode("utf-8")))
# b'w6l0w6k='
print(base64.b64encode("été".encode("utf-16")))
# b'//7pAHQA6QA='
एक जैसे चर, दो अलग बाइट स्ट्रिंग्स, दो अलग Base64 आउटपुट। कैरेक्टर सेट का चुनाव एक फैसला है, विवरण नहीं। UTF-8 वह डिफ़ॉल्ट है जो कुछ भी वायर, डेटाबेस, या API के पार जाए। Windows API से बात करते वक़्त UTF-16 सामने आता है, और वह सामने से एक बाइट-ऑर्डर मार्क लाता है जिसे आप एन्कोड नहीं करना चाहते; इसे utf-16-le इस्तेमाल करके या lstrip("\ufeff") से काटकर हटा सकते हैं। पुराने यूरोपीय फ़ाइलों में Latin-1 अब भी छिपा है, जहाँ एक चर बिल्कुल एक बाइट है और पूरा सवाल उठता ही नहीं। जो मानसिक मॉडल बनाए रखें: एन्कोडर कभी आपके टेक्स्ट को नहीं देखता; वह सिर्फ़ बिट्स देखता है। बाइट्स दीवार पार करते ही कैरेक्टर सेट का सवाल बंद हो जाता है - और यही वजह है कि डिकोडिंग वाली तरफ़ को बाद में पूछना पड़ता है कि वह कैरेक्टर सेट किसका था।
base64url: दो अक्षर बदलें, पैडिंग छोड़ें
स्टैंडर्ड वर्णमाला में दो चर छुपे हैं जिनसे URL और फ़ाइल सिस्टम की नफ़रत है। + चिह्न को कोई भी फ़ॉर्म डिकोडर ख़ामोशी से ख़ाली जगह के तौर पर पढ़ लेता है, और / चिह्न पथ-विभाजक है, इसलिए क्वेरी स्ट्रिंग या फ़ाइल-नाम में स्टैंडर्ड-वर्णमाला पेलोड एक टिकता हुआ बम है। RFC 4648 का खंड 5 फ़िक्स परिभाषित करता है: एक वैरिएंट जिसमें + हो जाता है - और / हो जाता है _, जहाँ भी डेटा की लंबाई संदर्भ से पता हो, पैडिंग छोड़ दी जाती है, और जिसे RFC ज़ोर देकर base64url कहने पर टिका है, सिर्फ़ "base64" नहीं। आप इसे JSON Web Tokens, OAuth टोकन, और API कर्सर पैरामीटर में मिलेंगे - यानी आधुनिक वेब के ज़्यादातर हिस्से में।
Python दोनों देता है: एक समर्पित फ़ंक्शन और पहले खंड वाला altchars नॉब, और दोनों का आउटपुट एक जैसा होता है:
import base64
data = b"\xfb\xff\xfe"
print(base64.b64encode(data))
# b'+//+'
print(base64.urlsafe_b64encode(data))
# b'-__-'
print(base64.b64encode(data, altchars=b"-_"))
# b'-__-'
टोकन और क्वेरी स्ट्रिंग में आमतौर पर पैडिंग भी जाती है, क्योंकि आख़िरी = को प्रतिशत-एन्कोडिंग की ज़रूरत होगी और कुछ मध्यवर्ती डिवाइस उसे आख़िर में कुचल ही देते हैं:
import base64
padded = base64.urlsafe_b64encode(b"fooba")
print(padded)
# b'Zm9vYmE='
print(padded.rstrip(b"="))
# b'Zm9vYmE'
हटाएँ, भेजें, और रिसीवर पैड वापस जोड़ लेता है मोड्यूलो चाल के साथ, "=" * (-len(s) % 4), जो बिल्कुल उतने ही पैड बनाता जितने लंबाई मांगती है। अनुभव का नियम: अगर डेटा URL, फ़ाइल-नाम, या JWT में बैठेगा, तो urlsafe वैरिएंट इस्तेमाल करें और पैड छोड़ दें; अगर ईमेल बॉडी या टेक्स्ट फ़ाइल में बैठेगा, तो पैडिंग वाला स्टैंडर्ड वर्णमाला ही सामान्य है।
जब आपका रीडर लाइन्स चाहता हो: MIME और 76-चर नियम
b64encode की एक अखंड लाइन JSON फ़ील्ड्स, हेडर, और URL के लिए बिल्कुल सही है, पर ईमेल के अपने विचार हैं। RFC 2045, MIME मानक, Base64 आउटपुट को अधिकतम 76 चरों की लाइनों में तोड़ना मांगता है, और Python के पुराने औजार बिल्कुल इसी को बनाने के लिए बनाए गए थे। encodebytes, जो Python 3.1 में जुड़ा, बाइट्स ऑब्जेक्ट के लिए वह लपेटन करता है:
import base64
wrapped = base64.encodebytes(b"x" * 100)
for line in wrapped.splitlines():
print(len(line), line[:12])
# 76 eHh4eHh4eHh4
# 60 eHh4eHh4eHh4
यंत्र-विधि थोड़ी प्यारी है। मॉड्यूल 57-बाइट चंक्स में एन्कोड करता है, स्थिरांक MAXBINSIZE, क्योंकि 57 बाइट्स बिल्कुल 76 चर बनते हैं, और आधुनिक CPython में हर लपटी लाइन सादा लाइन-फीड से ख़त्म होती है। RFC 2045 CRLF माँगता था, पर Python का LF आउटपुट पूरे इकोसिस्टम के हर डिकोडर स्वीकार कर लेता है, Python के अपने समेत। पुरानी फ़ाइल-से-फ़ाइल फ़ंक्शन encode वही लपेटन सीधे एक फ़ाइल हैंडल से दूसरे तक कर देता है, जिससे वह बड़ी फ़ाइलों के लिए साफ़ औजार बन जाता है जिन्हें आप मेमोरी में दो बार पकड़ना नहीं चाहते।
किस औजार को कब, संक्षेप में: b64encode उस हर चीज़ के लिए जो JSON फ़ील्ड, URL, हेडर, या डेटाबेस कॉलम में जाती है; encodebytes ईमेल बॉडी और PEM-शैली कवच के लिए; पुरानी encode तब जब आप बड़ी फ़ाइल स्ट्रीम कर रहे हों और लपेटन मुफ़्त चाहिए। ग़लत वाला चुनना एक क्लासिक बग है, क्योंकि JSON फ़ील्ड के अंदर एक ही बेग़ार लाइन ब्रेक काफ़ी है कि दूर के सख़्त डिकोडर को एक्ससेप्शन फेंकने पर मज़बूर कर दे।
बड़ा परिवार
base64 मॉड्यूल असल में base-N मॉड्यूल है, और वह पूरा RFC 4648 परिवार साथ में रखता है, साथ में कंप्यूटिंग दुनिया के दूसरे कोनों के कुछ रिश्तेदार भी। ज़्यादातर एक-लाइन ड्रॉप-इन हैं, उसी बाइट्स-इन-बाइट्स-आउट अनुबंध के लिए:
| फ़ंक्शन्स | वर्णमाला | यह कहाँ मिलेगा |
|---|---|---|
b16encode / b16decode |
0-9A-F |
"Base16" सिर्फ़ हैक्सडेसिमल है; मॉड्यूल की सबसे तेज़ राउंड-ट्रिप, हैश और UUID के लिए बिल्कुल सही |
b32encode / b32decode |
A-Z2-7 |
लाइसेंस की और एक्टिवेशन कोड; न तो 0, O, 1 या I है, इसलिए शब्द-दर-शब्द बोलकर सुनाने से भी बच निकलता है |
b32hexencode / b32hexdecode |
0-9A-V |
हैक्सडेसिमल वर्णमाला वाला Base32, Python 3.10 में जुड़ा; एन्कोडेड डेटा को शब्दकोशीय क्रम में क्रमबद्ध रखने लायक बनाता है |
a85encode / a85decode |
85 प्रिंट करने लायक चर | PostScript और PDF का ASCII85, Unix btoa यूटिलिटी का वंशज; Python 3.4 से मॉड्यूल में |
b85encode / b85decode |
85 प्रिंट करने लायक चर | git और Mercurial बाइनरी डिफ़ का Base85 फ़ॉर्मेट; यह भी Python 3.4 से |
z85encode / z85decode |
85 प्रिंट करने लायक चर | ZeroMQ का Z85, Python 3.13 में जुड़ा; डेटा को चार बाइट्स के समूहों में फ्रेम करता है |
इनमें से कोई भी उन नियमों को नहीं बदलता जो आपने पहले ही सीख लिए हैं: बाइट्स अंदर, बाइट्स बाहर, चुनने के लिए एक वर्णमाला, और दूसरी तरफ़ इंतज़ार करती मेल-खाने वाली डिकोड फ़ंक्शन। अमल में, जब इंसान को वैल्यू पढ़नी चाहिए तो b16, जब वैल्यू हाथ से टाइप या बोली जाए तो b32, और 85-चर फौजदियों के लिए सिर्फ़ तब, जब कोई स्पेसिफिकेशन कहें। बाकी सब के लिए, इस लेख की शुरुआत वाला Base64 जोड़ा ही सही औजार है, और यही वह है जिस पर बाकी हर हिस्सा खड़ा है।
पेज में तस्वीरें: Data URL
वेब पर सबसे ज़्यादा दिखने वाला Base64 data: URI है: मीडिया सीधे HTML या CSS में बैठा हुआ, ताकि ब्राउज़र दूसरा रिक्वेस्ट न छोड़े। फ़ॉर्मेट है data:, मीडिया टाइप, base64 शब्द, एक कॉमा, और एन्कोडेड बाइट्स। डिस्क की फ़ाइल से एक बनाना तीन-लाइन का काम है:
import base64
with open("logo.png", "rb") as handle:
encoded = base64.b64encode(handle.read()).decode("ascii")
uri = "data:image/png;base64," + encoded
print(uri[:40])
# data:image/png;base64,iVBORw0KGgoAAAAN...
दो सावधानियाँ, दोनों का पालन करना सस्ता। पहली, ब्राउज़र data URI की रेंडरिंग खुशी से करेगा, और दस्तावेज़ में उसके मेगाबाइट भी खुशी से थामे रखेगा: कुछ किलोबाइट से ज़्यादा की हर चीज़ के लिए, सही कैश हेडर वाला नॉर्मल इमेज रिक्वेस्ट हर उस मेट्रिक पर जीतता है जो मायने रखता है। दूसरी, पहले कॉलन के बाद का मीडिया टाइप एक वादा है। अगर बाइट्स JPEG हैं, तो URI कहता है image/jpeg, क्योंकि कुछ टूलिंग जोड़ी की जाँच करता है और कुछ रेंडरर ग़ुमानी करने से सीधे इनकार कर देते हैं। .decode("ascii") स्टेप भी सजावट नहीं है; बिना इसकी आप bytes ऑब्जेक्ट को स्ट्रिंग से जोड़ रहे हैं और TypeError समेट रहे हैं, टाइप की दीवार अपना गोल पूरा कर रही है।
वितरित करने योग्य टोकन: JWT
JSON Web Token तीन base64url टुकड़ों से बना होता है जो डॉट से जुड़े होते हैं: हेडर, पेलोड, और सिग्नेचर। अगर आप असली टोकन जारी कर रहे हैं, तो टुकड़े हाथ से मत बनाइए। PyJWT इंस्टॉल करें (pip install pyjwt) और base64url हिस्से, पैडिंग, और सिग्नेचर एक ही कॉल में बनने दें:
import jwt
# 32 बाइट्स से छोटी की PyJWT की InsecureKeyLengthWarning कमाती है, डिमो की के लिए एक उचित टिप्पणी।
token = jwt.encode(
{"sub": "1234567890", "name": "John Doe"},
"super-secret-key",
algorithm="HS256"
)
print(token)
# eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIi...
print(type(token))
# <class 'str'>
अंदर से PyJWT बिल्कुल वही कर रहा है जो यह लेख बताता है: JSON में सिरीलाइज़ करना, उसे urlsafe एन्कोडर से गुज़ारना, और पैड हटाना - RFC 7515 की JWS परिभाषा के अनुसार। अगर कभी किसी टेस्ट फ़िक्सचर या डीबगिंग सेशन के लिए टुकड़ा हाथ से बनाना हो, तो रेसिपी हर जगह वही अंकगणित है:
import base64
import json
payload = json.dumps({"sub": "1234567890"}).encode("ascii")
part = base64.urlsafe_b64encode(payload).rstrip(b"=")
print(part)
# eyJzdWIiOiAiMTIzNDU2Nzg5MCJ9
भरोसे की दिशा पर एक नोट: टोकन बनाना आसान आधा है। रिसीवर को एक भी क्लेम पर भरोसा करने से पहले सिग्नेचर की वेरिफिकेशन करनी होगी, और PyJWT 2.x बिना स्पष्ट algorithms सूची के टोकन को डिकोड ही नहीं करेगा - और यह एक फीचर है, क्योंकि "कोई भी एल्गोरिद्म" की ग़लती अब तक लिखे गए ऑथेंटिकेशन कोड की सबसे महँगी लाइनों में से एक है।
HTTP: Basic Auth और WebSocket हैंडशेक
HTTP के दो मोमेंट Base64 पर ज़िंदा-मरते हैं। पहला प्रोटोकॉल का सबसे पुराना ऑथेंटिकेशन स्कीम है: Basic ऑथ (RFC 7617), जिसमें क्लाइंट user:pass भेजता है, base64-एन्कोडेड, Basic शब्द के पीछे:
import base64
credentials = base64.b64encode(b"jane:pa:ss").decode("ascii")
header = "Basic " + credentials
print(header)
# Basic amFuZTpwYTpzcw==
अगर requests पहले से आपकी स्टैक में है, तो यह auth=("jane", "pa:ss") के साथ यह हेडर आपके लिए बना देता है, जिसका इस्तेमाल ज़रूर करें क्योंकि यह एन्कोडिंग विवरण आपके कोड से बाहर रखता है। और जो हो रहा है, उसके बारे में ईमानदार रहें: RFC 7617 ख़ुलकर कहता है कि यह स्कीम "उपयोगकर्ता ऑथेंटिकेशन के लिए सुरक्षित तरीका नहीं है, और न ही वह उस पहचान की किसी भी तरह से रक्षा करता है, जो सादे टेक्स्ट में भेजी जाती है"। क्रेडेंशियल्स को ट्रैफ़िक देखने वाला कोई भी एक लाइन कोड में रिकवर कर सकता है, इसलिए यह TLS-सुरक्षित कनेक्शन के लिए सुविधा है, सुरक्षा सीमा नहीं।
दूसरा मोमेंट WebSocket हैंडशेक (RFC 6455) है, जहाँ सर्वर यह सिद्ध करने के लिए कि उसने क्लाइंट की रैंडम की पढ़ी, की को एक मैजिक GUID से चिपकाकर उसके SHA-1 हैश का Base64 जवाब में देता है:
import base64
import hashlib
key = "dGhlIHNhbXBsZSBub25jZQ=="
magic = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
accept = base64.b64encode(
hashlib.sha1((key + magic).encode("ascii")).digest()
).decode("ascii")
print(accept)
# s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
आउटपुट RFC के अपने विस्तृत उदाहरण का बिल्कुल सही मान है, और शून्य से लिखे इम्पलीमेंटेशन की जाँच के लिए यह एक सुंदर तरीक़ा है। प्रोडक्शन में websockets लाइब्रेरी दोनों छोरों पर यह स्टेप आपके लिए कर देती है; आप बस तब हाथ से बनाते हैं जब वह छोटा सा टेस्ट सर्वर लिख रहे हों जो आपकी समझ की गवाही दे।
ईमेल: मूल ग्राहक
Base64 को 1993 में एक काम के लिए मानक बनाया गया था, और वह काम था ईमेल: बाइनरी को SMTP के सिर्फ़-टेक्स्ट दुनिया में जीवित रखना, RFC 2045 के Content-Transfer-Encoding: base64 के अनुसार। Python का email पैकेज मैसेज बनाता है, एन्कोडिंग चुनता है, और स्टैंडर्ड लाइन लंबाई पर बॉडी को लपेटता है - बिना आपकी एक भी Base64 लाइन के:
import email.mime.multipart
import email.mime.application
msg = email.mime.multipart.MIMEMultipart()
msg["Subject"] = "binary payload"
part = email.mime.application.MIMEApplication(
b"\x00\x01\x02", _subtype="octet-stream"
)
msg.attach(part)
text = msg.as_string()
print(text)
# ...
# Content-Transfer-Encoding: base64
#
# AAEC
# --...
MIMEApplication पार्ट वह दिलचस्प वाला है: यह बाइट्स को सही 76-चर लाइनों में लपेटता है और transfer-encoding हेडर पर मोहर लगाता है, जो बिल्कुल पहले वाला encodebytes व्यवहार है जिसे फ्रेमवर्क लागू करता है। अगर आप पूरा मैसेज के बजाय नंगा स्निपेट बना रहे हैं, तो email.encoders.encode_base64(obj) मैसेज ऑब्जेक्ट पर सीधे एक एन्कोड-ओर-लपेट कर देता है। और जब मैसेज दूसरे छोर पर पहुँचता है, क़िस्से की डिकोडिंग वाली तरफ़, get_payload(decode=True) से लेकर हेडर डिकोडिंग तक, वह जुड़े डिकोडिंग लेख में कवर है।
की और सर्टिफिकेट के लिए PEM कवच
PEM फ़ाइलें - सर्टिफिकेट, प्राइवेट कीज़, CRLs जो -----BEGIN ...----- से खुलते हैं - बस Base64 के चारों ओर कवच हैं: एक लेबल लाइन, लपटा हुआ Base64, एक बंद करने वाला लेबल। कवच पारदर्शी है, क्योंकि बॉडी बस वह लपटा हुआ आउटपुट है जिसे आप पहले मिल चुके हैं:
import base64
der = b"\x30\x03\x02\x01\x05" # उदाहरण के लिए एक छोटी-सी DER ब्लाब
body = base64.encodebytes(der).decode("ascii")
armor = ("-----BEGIN CERTIFICATE-----\n"
+ body
+ "-----END CERTIFICATE-----")
print(armor)
# -----BEGIN CERTIFICATE-----
# MAMCAQU=
# -----END CERTIFICATE-----
दो लेबल लाइनें हटाएँ, बाकी को जोड़ें, और b64decode DER बाइट्स वापस सौंप देता है। प्रोडक्शन में आप इसे हाथ से लगभग कभी नहीं करेंगे: cryptography पैकेज (pip install cryptography) public_bytes से कवच बनाता है और load_pem_x509_certificate जैसे साथियों से पार्स करता है, Base64 स्टेप आपके लिए अंदर से करके। मैन्युअल पथ उस विशेष क्षण में अपना खर्चा कमाता है जब कच्चे DER बाइट्स पहले से आपके हाथ में हों - एक डेटाबेस कॉलम, एक कॉन्फ़िग फ़ाइल, किसी प्रोटोकॉल का बफ़र - और सामने की स्पेसिफिकेशन कहती है "कृपया PEM"।
फ़ाइलें भेजना: अपलोड, डाउनलोड और .b64 की आदत
इंटरनेट का सबसे पुराना काम वह बाइनरी फ़ाइल है जो ऐसे चैनल से गुज़रनी पड़े जो सिर्फ़ टेक्स्ट ले जाते हैं: ऐसा FTP जो लाइन एंडिंग कुचल दे, ऐसा फ़ॉर्म जो अपलोड से इनकार करे, ऐसा चैट विंडो जो बाइनरी निगल ले। रेसिपी है: पढ़ो, एन्कोड करो, भेजो, और दूर के छोर को डिकोड करने दो - और इसका दिलचस्प आधा हिस्सा बीच के दो स्टेप हैं:
import base64
with open("photo.png", "rb") as src:
data = src.read()
wrapped = base64.encodebytes(data)
with open("photo.b64", "wb") as dst:
dst.write(wrapped)
print(len(wrapped), "bytes on disk for", len(data), "in the photo")
# मूल से लगभग एक-तिहाई बड़ा
दो नोट्स। .b64 एक्सटेंशन समुदाय की रवायत है, मानक नहीं, इसलिए रिसीव करने वाली तरफ़ को भी वह रवायत जाननी होगी - यही वजह है कि JSON API आमतौर पर पेलोड को "image_base64" जैसे नाम वाली फ़ील्ड में लपेटते हैं और अपने दस्तावेज़ीकरण में कह देते हैं। और encodebytes की लपटी हुई 76-चर लाइनें डिस्क पर फ़ाइल के लिए चुनी हुई फ़ॉर्मेट हैं, क्योंकि वे हर टेक्स्ट टूल में साफ़ कॉपी-पेस्ट हो जाती हैं, मेल क्लाइंट्स से लेकर PDF रीडर तक। उल्टी दिशा - ऐसी फ़ाइल को वापस बाइट्स में पढ़ना - जुड़े डिकोडिंग लेख में एक ही कॉल है; यहाँ आप बस भेजने वाले हैं, और भेजने वाले का काम है कि वह संगत रहे।
स्टोरेज का सवाल: कॉन्फ़िग फ़ाइलें, एनवाइरनमेंट वेरिएबल और डेटाबेस
डेवलपर Base64 को ऐसी जगह रखना पसंद करते हैं जो सिर्फ़ टेक्स्ट स्वीकार करती हैं: .env फ़ाइल, .ini सेटिंग, TEXT कॉलम। एन्कोडिंग का स्टेप हल्का-फुल्का है, और सबसे आम रूप है Base64 में बंद JSON:
import base64
import json
config = {"api_user": "svc-bot", "api_pass": "hunter2-not-really"}
packed = base64.b64encode(
json.dumps(config).encode("utf-8")
).decode("ascii")
print(packed)
# eyJhcGlfdXNlciI6ICJzdmMtYm90IiwgImFwaV9wYXNzIjogImh1bnRlcjItbm90LXJlYWxseSJ9
और फिर वह चेतावनी, क्योंकि पूरे इस लेख का सबसे महँगा भ्रम यहीं रहता है। Base64 ऐसा धुंधलापन नहीं है जो टिके, और न ही यह एन्क्रिप्शन है। RFC 4648 का खंड 12 इसे एक RFC की तरह सबसे साफ़-सादा कहता है: base एन्कोडिंग "देखने में ऐसी जानकारी को छिपाता है जो वरना आसानी से पहचानी जा सकती थी, जैसे पासवर्ड, पर यह कोई कंप्यूटेशनल गुप्तता नहीं प्रदान करता"। Base64 सिक्रेट वाली .env फ़ाइल उस इंसान से बचाती है जो इसे झाँककर देखता है, उस इंसान से नहीं जो उसे पढ़ता है, और base64 -d की एक कमांड बाद "सिक्रेट" उनके टर्मिनल में सादे टेक्स्ट में बैठा होता है। अगर डेटा सच में संवेदनशील है, तो पहले एन्क्रिप्ट करें - cryptography पैकेज बिल्कुल इसके लिए Fernet देता है - और तभी, अगर आपकी स्टोरेज टेक्स्ट मांगती है, सिफ़रपाठ को Base64 करें।
एक मिलियन बाइट्स बाद: बड़ा डेटा और चंकिंग
b64encode एक C-speed फ़ंक्शन है - एक आम लैपटॉप पर वह एक मेगाबाइट को लगभग एक मिलीसेकंड में प्रोसेस करता है - पर यह स्ट्रीमिंग फ़ंक्शन नहीं है। स्टैंडर्ड लाइब्रेरी में कहीं भी update-and-finish की जोड़ी नहीं है, इसलिए मेमोरी में पकड़ने से बड़ी डेटा को एन्कोड करने का मतलब है कि बाउंड्री का अंकगणित आप खुद करें। तीन इनपुट बाइट्स चार आउटपुट चर बनाते हैं, इसलिए कोई भी चंक बाउंड्री तीन-बाइट सीम पर गिरनी चाहिए:
import base64
def encode_chunks(chunks):
out = []
leftover = b""
for chunk in chunks:
buffer = leftover + chunk
whole = len(buffer) // 3 * 3
if whole:
out.append(base64.b64encode(buffer[:whole]))
leftover = buffer[whole:]
if leftover:
out.append(base64.b64encode(leftover))
return b"".join(out)
with open("video.mp4", "rb") as handle:
encoded = encode_chunks(iter(lambda: handle.read(65536), b""))
आउटपुट बाइट-दर-बाइट वही है जो पूरी फ़ाइल को एक कॉल में एन्कोड करने पर मिलेगा, क्योंकि तीन-बाइट सीम ही वह एकमात्र जगह है जहाँ ग्रुपिंग टूट सकती है। पैडिंग बिल्कुल एक बार आती है, आख़िरी चंक पर, और यही वह है जो दूर के सख़्त डिकोडर का इंतज़ार होगा। iter(lambda: handle.read(65536), b"") लाइन फ़ाइल को तय-साइज़ टुकड़ों में पढ़ने का मानक तरीक़ा है, और leftover वेरिएबल पूरा एल्गोरिद्म है। डिकोडिंग वाली तरफ़ तीन-बाइट के बजाय चार-चर सीम रखती है, इसलिए दोनों लेख उस अंकगणित को आपस में बाँट लेते हैं, दोहराने के बजाय।
जहाँ एन्कोडर ग़लती करते हैं
एन्कोडिंग वाली तरफ़ डिकोडिंग वाली तरफ़ से कम फँदा हैं, क्योंकि जब आप अक्षर बनाने वाले हैं तो ग़लती की जगह कम होती है। फिर भी, ये हर हफ़्ते सामने आते हैं, और हर एक का दो-मिनट का फ़िक्स है, अगर आप जल्दी पहचान लें:
- एन्कोडर को स्ट्रिंग खिला देना। टाइप-दीवार वाले खंड की
TypeError। बग की जड़ पर ही फ़िक्स करें.encode("utf-8")से, और टाइप करने से पहले सोचें कि आप असल में कौन सा कैरेक्टर सेट चाहते हैं। - यह भूल जाना कि आउटपुट बाइट्स है।
b64encodeबाइट्स लौटाता है; URL या JSON फ़ील्ड में वहstrजाता है, इसलिए.decode("ascii")स्टेप रेसिपी का हिस्सा है, बाद में याद आने वाली बात नहीं। - URL-safe बदलाव हाथ से कर लेना।
str.replace("+", "-").replace("/", "_")काम करता है, पर जहाँurlsafe_b64encodeएक कॉल है, वहाँ यह दो अक्षर का रखरखाव-दायित्व है। ज़्यादा बुरी बात, अधूरा बदलाव - प्लस ठीक हो गए, स्लैश भूल गए - ऐसी वर्णमाला बनाता है जो किसी भी स्पेसिफिकेशन से नहीं मिलती। - URL में पैड छोड़ देना। क्वेरी स्ट्रिंग के अंदर आख़िरी
=को एक टूल प्रतिशत-एन्कोड करता है और दूसरा हटा देता है, और रिसीवर की पैडिंग गणित सबसे भ्रामक तरह टूट जाती है। हटाएँ; लंबाई डिकोडर को सब बता देती है जिसकी ज़रूरत है। - वहाँ लपेट लेना जहाँ नहीं चाहिए।
encodebytesलाइन ब्रेक्स ईमेल और PEM के लिए सही हैं, और JSON फ़ील्ड या URL के लिए ज़हर। एक ही बेग़ार लाइन ब्रेक काफ़ी है कि दूर का सख़्त डिकोडर आपके फ़ॉर्मेटिंग के बजाय आपके डेटा के बारे में एक्ससेप्शन फेंके। - डबल-एन्कोडिंग। डेटा पहले से ऊपर की तरफ़ Base64 था - कोई फ़ील्ड जो दूसरे API से पहले से एन्कोडेड पहुँची, कोई फ़ाइल जो दो बार
.b64चक्कर से गुज़री - और दूसरी पारी ऐसी स्ट्रिंग बनाती है जो पहली एन्कोडिंग में वापस डिकोड होती है। एक बार एन्कोड-डिकोड चक्कर लगाएँ, मैजिक बाइट्स जाँचें, और रुक जाएँ। - सिक्रेट को Base64 पर भरोसा करना। स्टोरेज-खंड की चेतावनी, दोहराई गई क्योंकि इसका असली पैसा लगता है: अगर थ्रेट मॉडल में फ़ाइल पढ़ने वाला कोई भी हो, तो आपको सिफ़र चाहिए, वर्णमाला नहीं।
एक ऐसा चेंजलॉग जो वाकई पढ़ा जा सके
मॉड्यूल की उम्र क्रांतियों के बजाय ख़ामोश, तारीख़बंद सुधारों में दिखती है। संक्षिप्त रूप, उसी क्रम में जिस क्रम में चीज़ें उतरीं, एन्कोडर की नज़र से:
| वर्ज़न | क्या हुआ |
|---|---|
| Python 2.4 (2004) | Barry Warsaw का पूरा RFC 3548 सपोर्ट शिप होता है: b16, b32 और b64 परिवार, साथ ही आज उपयोग की जाने वाली standard_* और urlsafe_* वैरिएंट्स |
| Python 3.1 (2009) | encodebytes आता है और encodestring अप्रचलित हो जाता है, वह नाम-बदलाव जिसमें पुराने ट्यूटोरियल्स अभी भी अटकते हैं |
| Python 3.4 (2014) | हर एन्कोडर कोई भी बाइट्स जैसा ऑब्जेक्ट स्वीकार करता है, और a85encode तथा b85encode मॉड्यूल में शामिल हो जाते हैं |
| Python 3.6 (2016) | binascii.b2a_base64 को newline स्विच मिलता है, और यही b64encode को एक अखंड लाइन बनाए रखने देता है |
| Python 3.9 (2020) | पुराने encodestring और decodestring नाम आख़िरकार हटा दिए जाते हैं |
| Python 3.10 (2021) | b32hexencode और b32hexdecode, क्रमबद्ध हैक्सडेसिमल वर्णमाला वाले रिश्तेदार |
| Python 3.13 (2024) | z85encode और z85decode, ZeroMQ की वर्णमाला, परिवार में शामिल हो जाती हैं |
| Python 3.14 (2025) | स्टैंडर्ड लाइब्रेरी भर में तेज़ इम्पोर्ट, base64 समेत, और b16decode जो छह गुना तक तेज़ हो गया है, क्योंकि उसकी वेरिफिकेशन अब bytes.translate पर चलती है, रेगुलर एक्सप्रेशन के बजाय |
यह वह धागा है, अगर आपको चाहिए: 1995 में मॉड्यूल को फिर से लिखा गया ताकि अपना काम C-स्तर के binascii मॉड्यूल को सौंपा जाए, और वह सौपना आज भी सच है। bytes-युग का पहला बदलाव, Python 3 के विकास के दौरान 2007 का वह कमिट जिसने सब कुछ हर जगह बाइट्स इस्तेमाल करवा दिया, यहीं से इस लेख की टाइप-दीवार आई है, और यही वह वजह है कि आधुनिक एन्कोडर बाइट्स लेता है और बाइट्स वापस सौंपता है, बाकी सब उस एक अनुबंध के चारों ओर रैपर है।
जो मॉड्यूल आपको नहीं बताता
गंभीर काम पूरा हो गया, इसलिए यहाँ एन्कोडिंग वाले बही-खाते की तरफ़ के छोटे-छोटे मनोरंजन हैं:
- दस्तावेज़ीकरण का अपना उदाहरण दशक से ज़्यादा वक़्त से वही प्रदर्शन कर रहा है:
b'data to be encoded'अंदर जाता है,b'ZGF0YSB0byBiZSBlbmNvZGVk'बाहर आता है। आप पहले से ही इस जोड़ी से मिल चुके हैं, चाहे आपको पता हो या नहीं। b64encodeके नीचे वाला C फ़ंक्शन अपने आख़िरी न्यूलाइन को एक टिप्पणी के साथ जोड़ता है, जो कहती है "एक विनम्रता वाली न्यूलाइन जोड़ी जाएँ"। एक पूरी संस्कृति, सोर्स कोड की एक लाइन में।passwordशब्दcGFzc3dvcmQ=में एन्कोड होता है, और इसीलिए लॉग फ़ाइल में Base64 किसी स्कैनर के लिए सिक्रेट जैसा दिखता है, और किसी रीडर के लिए सिक्रेट बनने से सिर्फ़ एक कमांड दूर है।b64encodeकभी लपेटता नहीं। कभी नहीं। एक गिगाबाइट इनपुट एक ही 1.3-गिगाबाइट लाइन बनाता है, और फ़ंक्शन की पलक भी नहीं फड़कती। अगर लाइनें चाहिए थीं, तोencodebytesमाँगना पड़ता।- मॉड्यूल का डॉक्स्ट्रिंग अभी भी RFC 3548 का नाम लेता है, स्पेसिफिकेशन के 2003 संस्करण। RFC 4648 ने 2006 में कमान संभाली; डॉक्स्ट्रिंग को बस कभी पता नहीं चला।
- Python 2 में टाइप दीवार बिल्कुल नहीं थी:
b64encodeखुशी सेstrस्वीकार करता था और वही लौटाता था। 2007 की bytes-सफ़ाई ने उसे ख़त्म किया, और ज़्यादातर "मेरा एन्कोड क्यों क्रैश हो रहा है" थ्रेड अभी भी पुराने Python 2 ट्यूटोरियल्स की तरफ़ इशारा करते हैं। z85encode, परिवार का सबसे नया सदस्य (Python 3.13), सबसे चुटीला है: ZeroMQ डेटा को चार बाइट्स के समूहों में फ्रेम करता है, इसलिए स्पेसिफिकेशन एन्कोडेड आउटपुट को पाँच चरों का गुणज माँगता है - और दस्तावेज़ीकरण पैडिंग आप पर डाल देता है: इनपुट 4 बाइट्स का गुणज होकर ही आना चाहिए (एन्कोडर आपके लिए पैड नहीं करेगा; 3-बाइट इनपुट से एक 4-चर फ्रेम बनता है जिसे कोई ZeroMQ समकक्ष स्वीकार नहीं करेगा)।
तो एन्कोडर का दर्शन तीन नियमों में। पहले बाइट्स तय करें, कैरेक्टर सेट बाद में, क्योंकि टाइप-दीवार ही वह जगह है जहाँ ज़्यादातर Python Base64 बग जन्म लेते हैं। वर्णमाला डेटा के लिए नहीं, चैनल के लिए चुनें: ईमेल और फ़ाइलों के लिए पैड वाला स्टैंडर्ड, URL और टोकन के लिए पैड के बिना base64url, और कीबोर्ड पर कभी तीसरा वैरिएंट खुदबुनियादी न करें। और आउटपुट को उसी आकार में रखें जिसकी उसका रीडर उम्मीद करता है: JSON और हेडर के लिए एक लाइन, MIME और PEM के लिए 76-चर लाइनें, क्योंकि दूर का डिकोडर आपको इसी पर खड़ा करेगा।
जब वे अक्षर दूसरे छोर पर पहुँचते हैं, तो मज़ा असल में शुरू होता है: ग़ायब पैड, ख़ामोशी से फेंका जाने वाला, आधे-अधूरे Base64 पेलोड, और दो मूडों वाला डिकोडर जिसमें रास्ता निकालना पड़ता है। सब कुछ इस पेज के नीचे जुड़े Base64 डिकोडिंग लेख में विस्तार से कवर है, और दोनों गाइड जोड़ी में अच्छी पढ़ती हैं। खुशी से एन्कोड करें।
अंतिम अपडेट: 2026-09-08
संबंधित लेख: Python में Base64 डिकोडिंग: एक सम्पूर्ण गाइड