ज़्यादातर वेब ऐप्स इमेज अपलोड को एक 'ब्लैक बॉक्स' की तरह हैंडल करते हैं। यूज़र एक फ़ाइल ड्रॉप करता है, ब्राउज़र उसे भेज देता है, और सर्वर या तो पेलोड को स्वीकार कर लेता है या फिर 413 एरर दे देता है जिसके लिए कोई तैयार नहीं होता। ब्राउज़र-साइड कंप्रेशन इस समीकरण को बदल देता है। यह आपको पेलोड को वायर (wire) पर पहुँचने से पहले ही छोटा करने का मौका देता है, जिसका अर्थ है तेज़ अपलोड, कम बैंडविड्थ बिल और कम सर्वर टाइमआउट। लेकिन इस काम में गलती करना आसान है। अगर आप कंप्रेशन को केवल "quality" लेबल वाले किसी जादुई स्लाइडर की तरह समझेंगे, तो आप खराब तस्वीरें, खिंची हुई थंबनेल और भ्रमित करने वाला यूज़र एक्सपीरियंस पेश करेंगे। बेहतर तरीका यह है कि पूरे फ्लो को एक पाइपलाइन की तरह माना जाए।

स्लाइडर्स नहीं, पाइपलाइनों के बारे में सोचें

काम को अलग-अलग चरणों में बाँटें। इनपुट एलिमेंट से फ़ाइल पढ़ें। इमेज को अपने लक्षित (target) आयामों (dimensions) तक छोटा करें। एक नया Blob एनकोड करें। फिर परिणाम को वापस यूज़र को दिखाएं। प्रत्येक चरण एक काम करता है और अपना आउटपुट अगले चरण को सौंप देता है। यह अलगाव न केवल कोड को साफ़-सुथरा बनाता है, बल्कि यूनिट टेस्टिंग को भी सीधा बनाता है। आप फ़ाइल इनपुट को छुए बिना स्केलिंग चरण में एक ज्ञात बफ़र डाल सकते हैं। आप सर्वर के रिस्पॉन्स का इंतज़ार किए बिना यह सत्यापित कर सकते हैं कि आपका एनकोडर 200 KB से कम का JPEG बना रहा है। जब कुछ टूटता है, तो आपको पता होता है कि ठीक किस चरण में विफलता हुई।

इन कार्यों को अलग रखने से अपलोड के दौरान होने वाले सरप्राइज़ से भी बचाव होता है। यदि आप स्केलिंग और एनकोडिंग को एक उलझे हुए फंक्शन में मिला देते हैं, तो बीच में होने वाली डिकोडिंग एरर आपके अपलोड क्यू (queue) को एक असंगत स्थिति में छोड़ सकती है। एक पाइपलाइन आपको हर सीमा (boundary) पर सत्यापन करने के लिए मजबूर करती है। यदि फ़ाइल को डिकोड नहीं किया जा सकता है, तो आप कैनवास बनाने से पहले ही इसे पकड़ लेंगे। यदि एनकोडेड Blob बहुत बड़ा है, तो आप सर्वर को इसे स्टोर करने के लिए कहने से पहले ही इसे पकड़ लेंगे।

कोड लिखने से पहले एक कॉन्ट्रैक्ट तय करें

इससे पहले कि कोई canvas draw कॉल लिखे, नियम लिख लें और उन्हें टीम के साथ साझा करें। स्वीकृत MIME प्रकार चुनें। क्या आप JPEG, PNG, WebP, या AVIF की अनुमति देंगे? प्रत्येक का अल्फा चैनल, ब्राउज़र सपोर्ट और CPU लागत पर प्रभाव पड़ता है। अधिकतम इनपुट आकार निर्धारित करें। यदि आप किसी फ्लैगशिप फोन की 30 MB की रॉ फोटो को पूरी तरह से मेमोरी में डिकोड करने की कोशिश करते हैं, तो यह पुराने लैपटॉप को फ्रीज़ या क्रैश कर सकती है। अधिकतम आउटपुट आयाम (dimensions) परिभाषित करें। यदि आपका UI कभी भी 2048 पिक्सेल से अधिक चौड़ी इमेज नहीं दिखाता है, तो 6000 पिक्सेल चौड़ी फोटो को पाइपलाइन से गुजरने देने का कोई कारण नहीं है।

सबसे महत्वपूर्ण बात यह है कि डिकोडिंग विफलताओं के लिए योजना बनाएं। एक करप्ट फ़ाइल, एक अनोखा कलर प्रोफाइल, या एक अधूरा अपलोड Image कंस्ट्रक्टर पर एरर दे सकता है। आपकी पाइपलाइन में एक स्पष्ट catch ब्लॉक और मानव-पठनीय (human-readable) एरर मैसेज होना चाहिए। ब्राउज़र को चुपचाप क्रैश न होने दें और यूज़र को बिना कुछ हुए बस स्पिनर देखते रहने के लिए न छोड़ें।

इमेज का सम्मान करें

इमेज का बिगड़ना (Distortion) अनाड़ीपन की निशानी है। आस्पेक्ट रेशियो (aspect ratio) बनाए रखें और सबसे लंबी भुजा (side) को सीमित करें। यदि आपका लक्षित बॉक्स 1024 x 1024 पिक्सेल का है, तो 4000 x 3000 की फोटो को 1024 x 768 पर आना चाहिए, न कि 1024 x 1024 पर। लंबी भुजा से स्केल फैक्टर की गणना करें और छोटी भुजा को उसके अनुसार चलने दें। इससे इमेज अजीब आकारों में खिंचने से बच जाती है।

वास्तविक एक्सपोर्ट के लिए, कैनवास के toBlob मेथड का उपयोग करें। यह आपको आउटपुट फॉर्मेट और क्वालिटी सेटिंग पर सीधा नियंत्रण देता है, और यह एसिंक्रोनस (asynchronously) रूप से चलता है ताकि आप मेन थ्रेड को ब्लॉक न करें। एक ऑफस्क्रीन कैनवास बनाएं, उस पर रीसाइज की गई इमेज ड्रा करें, फिर अपनी पसंदीदा टाइप और क्वालिटी वैल्यू के साथ canvas.toBlob को कॉल करें। वह नया Blob ही है जिसे आप अपने अपलोड लॉजिक या स्टोरेज API को सौंपते हैं।

परिणाम दिखाएं

कंप्रेशन एक अदृश्य काम है। यदि आप आंकड़े सामने नहीं लाते हैं, तो यूज़र प्रक्रिया पर भरोसा नहीं करेंगे। एक ऐसा इंटरफ़ेस बनाएं जो उन्हें मूल (original) की तुलना परिणाम से करने दे। मूल फ़ाइल का आकार, नई फ़ाइल का आकार, नए आयाम और अंतिम फॉर्मेट टाइप दिखाएं। 4.2 MB की फोन फोटो को 380 KB WebP में बदलते देखना इस डर को दूर कर देता है कि आप गुप्त रूप से उनकी इमेज को खराब कर रहे हैं।

यह पारदर्शिता समस्या निवारण (troubleshooting) में भी मदद करती है। जब कोई यूज़र शिकायत करता है कि अपलोड विफल रहा, तो सबसे पहले आप यह जांचेंगे कि क्या आउटपुट आयाम आपके सर्वर की सीमा से अधिक थे, या क्या फॉर्मेट PNG से बदलकर JPEG हो गया और अल्फा चैनल हट गया। उस डेटा को UI में रखें ताकि यूज़र सपोर्ट टिकट खोलने से पहले खुद समस्या का निदान कर सके।

री-कंप्रेशन से बेहतर प्रीसेट्स हैं

एक ही इमेज को कभी भी दो बार कंप्रेस न करें। लॉस़ी (lossy) एनकोडर के माध्यम से हर बार गुजरने पर विवरण कम होता जाता है और ब्लॉकी आर्टिफैक्ट्स (blocky artifacts) आ जाते हैं। यदि आप यूज़र को बार-बार "optimize" बटन दबाने देते हैं, तो तीसरी पीढ़ी की इमेज फोटोकॉपी की फोटोकॉपी जैसी दिखेगी। इसके बजाय, मूल सोर्स फ़ाइल से हर आउटपुट जेनरेट करें और प्रीसेट्स पेश करें:

  • Smaller file: थंबनेल या तेज़ प्रीव्यू के लिए क्वालिटी को कम रखें और डाइमेंशन्स को सख्ती से सीमित करें।
  • Balanced: मध्यम क्वालिटी लेवल और उचित डाइमेंशन्स का लक्ष्य रखें, जो सोशल फीड और गैलरी के लिए उपयुक्त हो।
  • More detail: फोटोग्राफी, आर्टवर्क या प्रिंट प्रीव्यू के लिए हाई क्वालिटी बनाए रखें और बड़े डाइमेंशन्स सुरक्षित रखें।

ओरिजिनल Blob को मेमोरी में स्टोर करें ताकि यूजर क्वालिटी के बार-बार घटने (stacking generations of loss) की समस्या के बिना प्रीसेट्स के बीच स्विच कर सके। हमेशा सोर्स से ही जनरेट करें, पिछले आउटपुट से कभी नहीं।

वैसे टेस्ट करें जैसे आपके यूजर्स अपलोड करते हैं

फाइबर कनेक्शन और 32 GB RAM वाली आपकी डेवलपमेंट मशीन असलियत नहीं है। उन असली फाइलों के साथ टेस्ट करें जो वास्तविक यूजर्स इस्तेमाल करते हैं। iOS और Android के फोन फोटोज अलग-अलग मेटाडेटा ओरिएंटेशन का उपयोग करते हैं और HEIC सोर्स से भी हो सकते हैं। लोगो और आइकन जैसे ट्रांसपेरेंट एसेट्स JPEG कन्वर्जन के दौरान अलग तरह से व्यवहार करते हैं क्योंकि JPEG अल्फा चैनल्स को सपोर्ट नहीं करता है। बहुत बड़ी फाइलें 2 GB RAM वाले डिवाइस पर मेमोरी की सीमाओं को उजागर कर देंगी। धीमे मोबाइल CPU से यह पता चलेगा कि toBlob कॉल वास्तव में कितना समय लेता है।

CPU और नेटवर्क को थ्रॉटल करने के लिए Chrome DevTools का उपयोग करें। पांच साल पुराने Android फोन पर कोशिश करें। यदि एन्कोडिंग के दौरान आपका पाइपलाइन UI को तीन सेकंड के लिए लॉक कर देता है, तो आपको भारी काम को Web Worker में ले जाने की आवश्यकता है ताकि इंटरफ़ेस रिस्पॉन्सिव बना रहे।

पहले बुनियादी चीजों को शिप करें

हर फॉर्मेट को सपोर्ट करना लुभावना लगता है और