Yandex.Mail में नए अटैचमेंट

हम यह सुनिश्चित करने का प्रयास करते हैं कि Yandex.Mail के सभी भाग सभी उपयोगकर्ताओं के लिए समान रूप से अच्छी तरह से काम करते हैं। आज हम आपको बताएंगे कि कैसे और क्यों हम पूरी तरह से संलग्नक जोड़ने के लिए ब्लॉक को फिर से लिखते हैं। यह लेख एक फ्लैश को मना करने के बारे में है, आधुनिक ब्राउज़रों की क्षमताओं का समर्थन करता है और, परिणामस्वरूप, फ़ाइल डाउनलोड की गति और विश्वसनीयता बढ़ाता है।

समस्या

पहले, हमने पूरे Yandex.Mail दर्शकों को फ़्लैश के साथ और बिना विभाजित किया।

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

लेकिन बिना फ्लैश वाले उपयोगकर्ताओं (दैनिक दर्शकों के 8-10%) के लिए यह अधिक कठिन था। हमने सुझाव दिया कि वे /> साथ सामान्य फॉर्म के माध्यम से फाइलें अपलोड करें। पत्र की सामग्री के साथ एक iframe के माध्यम से इसमें से फाइलें भेजी गईं और इसमें बहुत समय लगा। "भेजें" बटन पर क्लिक करके, उपयोगकर्ता फ़ाइलों के अपलोड होने तक एक लंबा इंतजार करता था।

और यदि छोटी फ़ाइलों (25 एमबी तक) ने कोई विशेष कठिनाइयों का कारण नहीं बनाया, तो बड़े लोगों ने एक नई समस्या पैदा की: यदि फ़ाइल का आकार अनुमत सीमा से अधिक हो गया, तो मुझे Yandex.Narod सेवा और फिर Yandex.Disk का उपयोग करना पड़ा (हमने नए अनुलग्नकों के साथ फ़ाइल संग्रहण बदल दिया) *।

* भेजी गई फ़ाइलों के आकार की सीमा को Yandex.Mail में तकनीकी सीमाओं द्वारा इतना अधिक नहीं बताया गया है जितना कि तृतीय-पक्ष मेल सर्वरों की समस्याओं के कारण। उनमें से सभी बड़े अक्षरों को प्राप्त करने और संग्रहीत करने के लिए तैयार नहीं हैं। इस तरह के पत्रों को अभिभाषक तक पहुंचाने के लिए, हम Yandex.Disk पर 25 एमबी से बड़े अटैचमेंट को सहेजते हैं और पत्र के लिंक जोड़ते हैं।

बिना फ्लैश के उपयोगकर्ताओं के लिए फ़ाइल का आकार निर्धारित करने के लिए, हमने एक आंतरिक सेवा उठाई जो इस तरह से काम करती है: ग्राहक ने एक विशेष url के अनुरोध के साथ एक POST फ़ाइल भेजी, सर्वर ने अनुरोध की सामग्री-लंबाई हेडर पढ़ा और कनेक्शन बंद कर दिया।

सभी ब्राउज़रों में फ़ाइल अपलोड कार्यान्वयन को डिज़ाइन किया गया है ताकि यह पूरी तरह से फ़ाइल भेजने तक सर्वर से प्रतिक्रिया की उम्मीद न करे। इसलिए, सर्वर फ़ाइल का आकार तुरंत रिपोर्ट नहीं कर सकता। इस समस्या को हल करने के लिए, हमने एक दूसरा GET अनुरोध किया, जिसमें सर्वर ने क्लाइंट को डाउनलोड की गई फ़ाइल के आकार के बराबर सामग्री-लंबाई हेडर मान दिया।

उपयोगकर्ताओं को दोनों श्रेणियों के लिए ईमेल में फाइलें संलग्न करने में समस्या हो सकती है। उदाहरण के लिए, फ्लैश लोडर, हालांकि यह आपको कई फ़ाइलों का चयन करने की अनुमति देता है और उनका आकार निर्धारित कर सकता है, लेकिन:
  1. यह एक तृतीय-पक्ष प्लग-इन है जिसे उपयोगकर्ता के कंप्यूटर पर इंस्टॉल किया जाना चाहिए, और इसे अन्य प्लग-इन या एक्सटेंशन द्वारा अवरुद्ध किया जा सकता है;
  2. एसएसएल कनेक्शन और सुरक्षा के साथ समस्याएं हैं;
  3. फ़ाइलों को डाउनलोड करते समय समस्याओं और त्रुटियों को हल करना मुश्किल है।

और सामान्य /> कम से कम कोई मल्टीटास्किंग नहीं है।

बेशक, हम इस मामले से खुश नहीं थे, और हमने इन समस्याओं के प्रभावी समाधान की खोज करना बंद नहीं किया।

अवसर

पिछले एक साल में, सभी ब्राउज़रों ने सीखा है कि स्वतंत्र रूप से फ़ाइलों के साथ काम कैसे व्यवस्थित किया जाए (तीसरे पक्ष के प्लग-इन को जोड़े बिना)। आप मोज़िला डेवलपर नेटवर्क वेबसाइट पर एक लेख में उनकी सभी आधुनिक क्षमताओं पर करीब से नज़र डाल सकते हैं।

यहां HTML5 के विकास के साथ नई सुविधाएँ दी गई हैं:
- इनपुट टैग में कई गुण (क्रोम 4, फ़ायरफ़ॉक्स 3.6, IE 10, ओपेरा 11, सफारी 5 के साथ शुरू);
- खींचें और ड्रॉप एपीआई (क्रोम 4, फ़ायरफ़ॉक्स 3.5, IE 5.5, ओपेरा 12, सफारी 3);
- फॉर्मडाटा (क्रोम 7, फ़ायरफ़ॉक्स 4, आईई 10, ओपेरा 12, सफारी 5);
- XMLHttpRequest स्तर 2 + CORS + प्रगति ईवेंट (Chrome 7, फ़ायरफ़ॉक्स 4, IE 10, ओपेरा 12, सफारी 5)।

सैद्धांतिक रूप से, हम उन्हें डेढ़ साल पहले लागू कर सकते थे, लेकिन बदलाव केवल क्रोम और फ़ायरफ़ॉक्स को प्रभावित करेंगे। इन ब्राउज़रों की कुल हिस्सेदारी अच्छी थी, लेकिन वे एकाधिकार नहीं थे। ओपेरा और IE ने अभी तक इन सुविधाओं का समर्थन नहीं किया है। तो, आधे दर्शकों को अभी भी एक फ्लैश पर छोड़ना होगा।

इसलिए हमने इंतजार किया। और ओपेरा 12 के जून रिलीज से पहले आ रहा था, जिसमें आवश्यक तकनीकों को पेश करना संभव था, उन्होंने विकास शुरू किया।

IE10 के लिए, इसकी रिलीज निकट भविष्य में होने की उम्मीद है।

कार्यान्वयन

जैसा कि ऊपर उल्लेख किया गया है, हमें फ़ाइलों को बड़े और छोटे में अलग करना होगा। उदाहरण के लिए, एक उपयोगकर्ता एक ईमेल में दस फाइलें संलग्न करने की कोशिश कर रहा है, जिनमें से नौ कुल स्वीकार्य सीमा में फिट होते हैं, और दसवां बाकी के मुकाबले दोगुना है। फ़ाइलों को व्यक्तिगत रूप से अपलोड करने की क्षमता के बिना, सभी दस फाइलें Yandex.Disk में जाएंगी। हालांकि, यह उचित नहीं लगता है - डिस्क, पिछले एक को केवल एक फ़ाइल भेजना बेहतर है, और शेष को एक पत्र पर अपलोड करें। इसलिए हमने प्रत्येक फ़ाइल को अलग से डाउनलोड करने का निर्णय लिया।

आमतौर पर फाइलें मानक फॉर्म के माध्यम से अपलोड की जाती हैं:
 <form action="/upload" method="post"> <input type="file" multiple="true"/> <input type="submit"/> </form> 

मान लीजिए कि हम एक छिपे हुए iframe में एक फॉर्म जमा करते हैं। इस मामले में, ब्राउज़र इनपुट से सभी चयनित फ़ाइलों को पढ़ेगा (भले ही उनमें से बहुत सारे हों) और इसे पोस्ट / अनुरोध के साथ पोस्ट / अनुरोध के साथ भेजें। लेकिन यहां फाइलें एक साथ भरी हुई हैं, लेकिन यह हमें शोभा नहीं देता है।

आइए देखें कि कैसे AJAX हमारी मदद करता है। AJAX के माध्यम से फाइल भेजने के लिए, हमें फ़ॉर्मडैट समर्थन की आवश्यकता है। इसके बिना, आप फ़ाइलों को इनपुट में नहीं पढ़ सकते हैं और उन्हें अनुरोध में जोड़ सकते हैं। आइए इसे आज़माएँ:
 var formElement = document.getElementById("myFormElement"); var xhr = new XMLHttpRequest(); xhr.open("POST", "/upload", true); xhr.send(new FormData(formElement)); 

लेकिन इस मामले में, सभी फाइलें अभी भी इनपुट से भेजी जाएंगी। यह पता चला है कि आपको प्रत्येक फ़ाइल को अलग से लेने और यह निर्धारित करने की आवश्यकता है कि इसे कहाँ डाउनलोड करना है (डिस्क या एक पत्र में), अर्थात, इसे स्वतंत्र रूप से संसाधित करें।
 for (var i = 0, j = input.files.length; i < j; i++) { upload(input.files[i]); } function upload(file) { var url = ""; if (file.size > MESSAGE_LIMIT) { url = "uploader.disk.yandex.ru"; } else { url = "uploader.mail.yandex.ru"; } var data = new FormData(); data.append("attachment", file); var xhr = new XMLHttpRequest(); xhr.open("POST", url, true); xhr.send(data); } 

फ़ाइलों को व्यक्तिगत रूप से अपलोड करना भी सुविधाजनक है क्योंकि एक फ़ाइल को लोड करने में त्रुटि दूसरों के साथ हस्तक्षेप नहीं करती है।

हम सभी लोकप्रिय ब्राउज़रों का समर्थन करते हैं, लेकिन उनमें से सभी आधुनिक तकनीकों का समर्थन नहीं करते हैं। फ़ीचर डिटेक्शन पॉलिसी के अनुसार, हमने नई सुविधाओं को सक्षम करने के लिए चार चेक जोड़े हैं:
  1. FormData के लिए कोई समर्थन नहीं -> iframe का उपयोग करें।
  2. FormData के लिए समर्थन है -> AJAX का उपयोग करें।
  3. ड्रैग-एन-ड्रॉप और फॉर्मडाटा के लिए समर्थन है → फ़ाइल प्रबंधक से फ़ाइलों को खींचने और छोड़ने की क्षमता सक्षम करें। उदाहरण के लिए, IE में पहला है, लेकिन दूसरा नहीं है, इसलिए हम किसी भी तरह से खींची गई फ़ाइलों को नहीं भेज सकते हैं।
  4. कई इनपुट और फॉर्मडेटा के लिए समर्थन है -> कई फ़ाइलों का चयन करने की क्षमता सक्षम करें। उदाहरण के लिए, ओपेरा 11.6 में एक से अधिक इनपुट है, लेकिन कोई फॉर्मडाटा नहीं है, क्रमशः, हम एक बार में एक फाइल नहीं भेज सकते हैं।

तीसरे और चौथे चेक के परिणामस्वरूप मॉडर्निज़र के लिए परीक्षण किए गए:
 Modernizr .addTest('draganddrop-files', function() { return !!(Modernizr['draganddrop'] && window['FormData'] && window['FileReader']); }) .addTest('input-multiple', function() { return !!(Modernizr['input']['multiple'] && window['FormData'] && window['FileReader']); }); 

विंडोज के लिए सफारी 5.1 में, एक बग तुरंत मिल गया था: जब कई फ़ाइलों का चयन किया गया था, तो वे सभी शून्य आकार के हो गए और सर्वर को खाली भेज दिया। इस ब्राउज़र को सभी नई सुविधाएँ बंद करनी थीं।

AJAX परिवहन के अलावा, हमने एक सुंदर प्रगति बार को प्रस्तुत करने के लिए प्रगति घटनाओं का उपयोग करना शुरू कर दिया।

हम इसे इस तरह उपयोग करते हैं:
 var xhr = new XMLHttpRequest(); xhr.open('POST', '/upload', true); if (xhr.upload) { xhr.upload.addEventListener('progress', processProgressEvent, false); } else { drawCommonProgressbar() } 

ध्यान दें कि सर्वर पर डेटा लोड करते समय, घटना हैंडलर को xhr.upload संपत्ति पर लटका दिया जाना चाहिए, और सर्वर से डेटा लोड करते समय - xhr पर ही।

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

फ़ाइल एपीआई के लिए समर्थन के बिना ब्राउज़रों में (और कम और कम हैं), हम आंतरिक फ़ाइल साइजिंग सेवा का उपयोग करने से चूक रहे हैं।

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

हमें इस समस्या को हल करना था: केवल फ़ाइलों को डाउनलोड करने की क्षमता मेल में कैसे छोड़े?

ब्राउज़र स्वयं बहुत कुछ करता है, लेकिन सभी नहीं। Event.dataTransfer.files प्रॉपर्टी में ड्रॉप ईवेंट में, निश्चित रूप से, फ़ाइल सिस्टम से केवल ऑब्जेक्ट होंगे। लेकिन ये ऑब्जेक्ट फोल्डर और फाइल दोनों हो सकते हैं। फ़ोल्डरों के डाउनलोड को प्रतिबंधित करने के लिए (सभी ब्राउज़र फ़ोल्डर्स से फ़ाइलों को लोड नहीं कर सकते हैं - क्रोम 21 पहले था, और फ़ायरफ़ॉक्स ने इस सिद्धांत के साथ ऐसा करने से इनकार कर दिया ) हम फाइलरेडर का उपयोग करते हैं। यह एपीआई आपको डिस्क से एक फ़ाइल पढ़ने और जावास्क्रिप्ट में इसके साथ काम करने की अनुमति देता है। और अगर ऑब्जेक्ट पढ़ा जाता है, तो यह एक फाइल है। एक छोटा फ़ंक्शन जो इस विधि को लागू करता है, उसे GitHub पर देखा जा सकता है
 function isRegularFile(file, callback) { //   ,  4,     if (file.size > 4096) { callback(true); return; } if (!window['FileReader']) { //   callback(null); } else { try { var reader = new FileReader(); reader.onerror = function() { reader.onloadend = reader.onprogress = reader.onerror = null; // Chrome (Linux/Win), Firefox (Linux/Mac), Opera 12.01 (Linux/Mac/Win) callback(false); }; reader.onloadend = reader.onprogress = function() { reader.onloadend = reader.onprogress = reader.onerror = null; //   abort     if (e.type != 'loadend') { //      reader.abort(); } callback(true); }; reader.readAsDataURL(file); } catch(e) { // Firefox/Win callback(false); } } } 

हालांकि, यह चेक सभी ब्राउज़रों के लिए आवश्यक नहीं है - विंडोज के लिए क्रोम और मैक 8 के लिए IE10 स्वयं फ़ोल्डर्स को फ़िल्टर करें।

FileReader को बहुत सावधानी के साथ व्यवहार किया जाना चाहिए, विशेष रूप से Chrome में, जो अस्थिर व्यवहार करता है: 21 वें संस्करण तक, कई सौ मेगाबाइट की फ़ाइल पढ़ते समय टैब गिराए जाते हैं, और 21 वीं में यह छोटी फ़ाइलों पर गिरना शुरू हो जाता है। यहां तक ​​कि हमें इस ब्राउज़र के लिए FileReader का उपयोग छोड़ना पड़ा।

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

ड्रैगओवर और ड्रैगेंटर हैंडलर में इस समस्या को हल करने के लिए, हमने निम्नलिखित जांच की:
 var types = event.dataTransfer.types; if (types) { for (var i = 0, j = types.length; i < j; i++) { if (types[i] == 'Files') { showDragArea(); return false; } } } 

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

यह भी पता चला है कि घटनाओं ड्रैगेंटर, ड्रैगओवर और ड्रैगवेल, यदि उन्हें एक दस्तावेज़ पर लटका दिया जाता है, तो माउसओवर, माउसआउट जैसी ही समस्याओं के अधीन होते हैं: वे हर बार जब आप डोम नोड्स के बीच चलते हैं, तो फेंक दिए जाते हैं।

इन घटनाओं को संभालने के लिए समयबाह्य से समस्या हल हो गई थी।
 var processTimer = null; $(document).on({ 'dragover dragenter': function() { window.clearTimeout(processTimer); showDragArea(); }. 'dragleave': function() { processTimer = window.setTimeout(function() { hideDragArea(); }, 50); } }); 

क्रॉस डोमेन अनुरोध

डिस्क पर अपलोड करने के लिए, हमें क्रॉस-डोमेन क्वेरी समर्थन की आवश्यकता है, जिसे निम्नानुसार चेक किया जा सकता है:
 window['XMLHttpRequest'] && 'withCredentials' in new XMLHttpRequest() 

परिवहन को परिभाषित करने की नीति एक समान है।

क्रॉस-डोमेन अनुरोधों के लिए, आपको "प्रीफ़लाइट" विकल्प अनुरोधों का सही प्रसंस्करण करने की आवश्यकता है। इन अनुरोधों में, ब्राउज़र रिमोट सर्वर से पूछता है कि क्या इसे वर्तमान डोमेन से एक्सेस किया जा सकता है। वे कुछ इस तरह दिखते हैं:
 OPTIONS /upload HTTP/1.1 Host: disk-storage42.mail.yandex.net Origin: https://mail.yandex.ru Access-Control-Request-Method: POST Access-Control-Request-Headers: origin, content-type 

सर्वर को इस तरह से हल करने वाले हेडर के साथ प्रतिक्रिया करनी चाहिए, उदाहरण के लिए, इस तरह:
 Access-Control-Allow-Origin: https://mail.yandex.ru Allow: POST, PUT, TRACE, OPTIONS 

ऐसे अनुरोध हमेशा नहीं होते हैं, लेकिन उन्हें याद किया जाना चाहिए और सत्यापित किया जाना चाहिए कि वे सही तरीके से संसाधित हैं।

यदि ब्राउज़र को क्रॉस-डोमेन अनुरोध के लिए अनुमति नहीं मिलती है, तो अनुरोध स्थिति = 0 के साथ समाप्त हो जाएगा (इसे onreadystatechange में संसाधित किया जा सकता है)। इसका अर्थ यह भी हो सकता है कि उपयोगकर्ता या सर्वर द्वारा अनुरोध को बाधित किया गया था। किसी भी मामले में, आपको iframe लोडिंग पर एक कमबैक करना चाहिए।

Yandex.Disk को फाइल अपलोड करने की प्रक्रिया स्वयं इस तरह दिखती है: सबसे पहले, एक अनुरोध किया जाता है जिसमें ड्राइव का बैकएंड हमें url देता है जिसके द्वारा फाइल को रिपॉजिटरी में अपलोड किया जाता है, साथ ही साथ oid (ऑपरेशन आईडी), जिसके द्वारा आप ऑपरेशन की स्थिति का अनुरोध कर सकते हैं। डाउनलोड करना एक समकालिक ऑपरेशन नहीं है, और क्लाइंट से फाइल भेजने के अंत का मतलब यह नहीं है कि फाइल सर्वर पर तैयार है, इसे सही जगह पर सहेजा जाना चाहिए, एंटीवायरस द्वारा चेक किया गया, और डेटाबेस को लिखा गया है।

यदि प्रगति की घटनाओं के लिए समर्थन है, तो फ़ाइल डाउनलोड होने तक संचालन की स्थिति का अनुरोध नहीं किया जाता है, और ब्राउज़र का उपयोग करके प्रगति पट्टी तैयार की जाती है। यह आपको सर्वर पर लोड को कम करने और एक चिकनी प्रगति आकर्षित करने की अनुमति देता है।

यदि प्रगति की घटनाओं का समर्थन नहीं किया जाता है, तो हम हर एक से दो सेकंड में एक डाउनलोड स्थिति का अनुरोध करते हैं जब तक कि सर्वर यह नहीं कहता कि फ़ाइल तैयार है।

सफलता

हमारी राय में, खेल मोमबत्ती के लायक था। वर्तमान समाधान पूरी तरह से हमें सूट करता है, जिसमें एक फ्लैश होने के फायदों को खोए बिना, हमने कई समस्याओं को हल किया है:

Source: https://habr.com/ru/post/In154151/


All Articles