केस स्टडी - Chrome में डाउनलोड को खींचें और छोड़ें

परिचय

HTML 5 की कई बेहतरीन सुविधाओं में से एक, 'खींचें और छोड़ें' (DnD) है. यह सुविधा Firefox 3.5, Safari, Chrome, और IE पर काम करती है. Google ने हाल ही में एक नई सुविधा लॉन्च की है. इसकी मदद से, Google Chrome के उपयोगकर्ता ब्राउज़र से फ़ाइलों को खींचकर डेस्कटॉप पर छोड़ सकते हैं. यह सुविधा काफ़ी सुविधाजनक है. हालांकि, इस बारे में तब तक ज़्यादा जानकारी नहीं थी, जब तक कि रयान सेडन ने इस नई सुविधा पर अपनी रिवर्स इंजीनियरिंग की खोजों के बारे में एक लेख पोस्ट नहीं किया.

Box.net में हम इस बात को लेकर काफ़ी उत्साहित हैं कि इन नई सुविधाओं की मदद से, हम अपने क्लाउड कॉन्टेंट मैनेजमेंट समाधान को कैसे बेहतर बना सकते हैं. साथ ही, डेवलपर कम्यूनिटी में ज़्यादा योगदान दे सकते हैं. हमें यह बताते हुए खुशी हो रही है कि DnD Download को हमारे प्रॉडक्ट में इंटिग्रेट कर दिया गया है. अब Box के उपयोगकर्ता, फ़ाइल को डाउनलोड और सेव करने के लिए, उसे सीधे Chrome ब्राउज़र से अपने डेस्कटॉप पर खींचकर ला सकते हैं.

मैं आपको बताना चाहता/चाहती हूं कि इस नई सुविधा को डेवलप करने के दौरान, मुझे कई बार क्या-क्या करना पड़ा.

खींचें और छोड़ें सुविधा के साथ काम करने वाले एपीआई की जांच करना

सबसे पहले, यह देखें कि आपका ब्राउज़र, HTML5 ड्रैग और ड्रॉप की सुविधा के साथ पूरी तरह से काम करता है या नहीं. किसी सुविधा की जांच करने के लिए, Modernizr नाम की लाइब्रेरी का इस्तेमाल करना सबसे आसान तरीका है:

if (Modernizr.draganddrop) {
// Browser supports native HTML5 DnD.
} else {
// Fallback to a library solution.
}

पहला इटरेशन

मैंने सबसे पहले वह तरीका आज़माया जो सेडन को Gmail में मिला था. मैंने फ़ाइलों के ऐंकर लिंक में, 'data-downloadurl' नाम का एक नया एट्रिब्यूट जोड़ा है. इस प्रोसेस में, एचटीएमएल5 के कस्टम डेटा एट्रिब्यूट का इस्तेमाल किया जाता है. data-downloadurl में, आपको फ़ाइल का एमआईएम टाइप, डेस्टिनेशन फ़ाइल का नाम (डाउनलोड की गई फ़ाइल का पसंदीदा नाम), और फ़ाइल का डाउनलोड यूआरएल शामिल करना होगा. इसलिए, इसे एचटीएमएल टेंप्लेट में जोड़ा जाता है:

<a href="#" class="dnd"
data-downloadurl="{$item.mime}:{$item.filename}:{$item.url}"></a>

इससे ऐसा आउटपुट बनेगा:

<a href="#" class="dnd" data-downloadurl=
"image/jpeg:Penguins.jpg:https://www.box.net/box_download_file?file_id=f66690"></a>

मैंने एक jQuery plugin जोड़ा है, जो ब्राउज़र की सुविधाओं का पता लगाता है. यह प्लगिन, सेडन के लेख पर आधारित है, जिसे वॉन शोर्श ने बनाया है. हाइलाइट की गई लाइनें, मैंने फ़ॉन शोर्श के वर्शन में जोड़ी हैं:

(function($) {

$.fn.extend({
dragout: function() {
var files = this;
if (files.length > 0) {
    $(files).each(function() {
    var url = (this.dataset && this.dataset.downloadurl) ||
                this.getAttribute("data-downloadurl");
    if (this.addEventListener) {
        this.addEventListener("dragstart", function(e) {
        if (e.dataTransfer && e.dataTransfer.constructor == Clipboard &&
            e.dataTransfer.setData('DownloadURL', 'http://www.box.net')) {
            e.dataTransfer.setData("DownloadURL", url);
        }
        },false);
    }
    });
}
}
});

})(jQuery);

मैंने ऐसा इसलिए किया, क्योंकि ब्राउज़र की पहचान किए बिना, IE में एचटीएमएल एलिमेंट पर addEventListener() करने से JavaScript गड़बड़ी होगी. ऐसा इसलिए, क्योंकि IE अपने attachEvent() तरीके का इस्तेमाल करता है. फ़िलहाल, IE में e.dataTransfer की कोई वैल्यू नहीं है. Firefox (Mozilla) में e.dataTransfer.constructor, DataTransfer दिखाता है. वहीं, Webkit ब्राउज़र (Chrome और Safari) में क्लिपबोर्ड कन्स्ट्रक्टर लागू होता है. Safari में, e.dataTransfer.setData('DownloadURL','http://www.box.net') इस स्टेटमेंट के लिए गलत वैल्यू दिखाता है और Chrome सही वैल्यू दिखाता है. ऊपर बताए गए सभी टेस्ट करने के बाद, यह सुविधा सिर्फ़ Chrome पर उपलब्ध होगी. आपके पास यह तर्क देने का विकल्प है कि मैं ये काम कर सकता/सकती हूं:

/chrome/.test( navigator.userAgent.toLowerCase() )

हालांकि, मुझे ब्राउज़र के बजाय सुविधा का पता लगाने की सुविधा पसंद है. हालांकि, तकनीकी तौर पर यह पता नहीं चलता कि डीएनडी डाउनलोड की सुविधा काम करेगी या नहीं.

पहले चरण की समस्याएं

1) फ़िलहाल, फ़ाइलों को एक फ़ोल्डर से दूसरे फ़ोल्डर में ले जाने/कॉपी करने के लिए, पेज पर DnD की सुविधा चालू है. इसलिए, हमें DnD डाउनलोड और पेज पर DnD की सुविधा के बीच अंतर करने का तरीका चाहिए. तकनीकी तौर पर, हम इन दोनों कार्रवाइयों को एक साथ नहीं कर सकते. हम यह अनुमान नहीं लगा सकते कि उपयोगकर्ता, Box.net खाते में मौजूद किसी फ़ाइल को किसी दूसरे फ़ोल्डर में ले जाना चाहता है या उसे अपने डेस्कटॉप पर खींचकर ले जाना चाहता है. ये दोनों कार्रवाइयां पूरी तरह से अलग हैं. इसके अलावा, यह पता लगाना आसान नहीं है कि कर्सर ब्राउज़र विंडो के बाहर है या नहीं. दस्तावेज़ में mouseout इवेंट अटैच करने के लिए, window.onmouseout (IE) और document.onmouseout (अन्य ब्राउज़र) का इस्तेमाल किया जा सकता है. साथ ही, यह देखा जा सकता है कि e.relatedTarget.nodeName == "HTML" (e, mouseout इवेंट या window.event, जो भी उपलब्ध हो) है या नहीं. हालांकि, इवेंट बबल होने की वजह से ऐसा करना काफ़ी मुश्किल है. किसी इमेज या लेयर पर कर्सर घुमाने पर, इवेंट अपने-आप ट्रिगर हो सकता है. ऐसा खास तौर पर, Box.net जैसे जटिल वेब ऐप्लिकेशन में होता है.

2) हम चाहते हैं कि उपयोगकर्ता साफ़ तौर पर कुछ ऐसा करे जिससे वह गलती से किसी आइटम को डेस्कटॉप पर खींचकर न ले जाए. Box फ़ोल्डर का एडिटर, ऐसी एक्ज़ीक्यूटेबल फ़ाइल अपलोड कर सकता है जो डाउनलोड करने वाले व्यक्ति के कंप्यूटर पर कुछ गलत काम कर सकती है. हम चाहते हैं कि उपयोगकर्ता को यह पता हो कि फ़ाइल, डेस्कटॉप पर कब डाउनलोड होगी.

दूसरा इटरेशन

हमने कंट्रोल + खींचें और छोड़ें (Windows Ctrl बटन दबाकर फ़ाइल को खींचें और छोड़ें) सुविधा को आज़माने का फ़ैसला किया है. यह कार्रवाई, Windows डेस्कटॉप पर फ़ाइल का डुप्लीकेट बनाने के लिए की जाने वाली कार्रवाई से मेल खाती है. गलती से फ़ाइलें डाउनलोड होने से रोकने के लिए, उपयोगकर्ता को ज़्यादा काम करना पड़ता है. हालांकि, इसके लिए उसे कोई अतिरिक्त चरण पूरा नहीं करना पड़ता.

पहले चरण में इस्तेमाल किए गए jQuery प्लग इन को अब बंद कर दिया गया है. ऐसा इसलिए किया गया है, क्योंकि हमें पेज पर मौजूद डीएनडी को डीएनडी डाउनलोड के साथ बेहतर तरीके से इंटिग्रेट करना है. जिन लोगों को इस बारे में जानना है उनके लिए, हम jQuery UI के Draggable प्लग इन के बदले हुए वर्शन का इस्तेमाल करते हैं. टारगेट एलिमेंट के mousedown इवेंट में, हम यह कोड डालते हैं:

// DnD to desktop when the Ctrl key is pressed while dragging
if (e.ctrlKey) {
var that = $(e.target);
// make sure it is not IE (attachEvent).
if (that[0].addEventListener) {
    that[0].addEventListener("dragstart",function(e) {
        // e.dataTransfer in Firefox uses the DataTransfer constructor
        // instead of Clipboard
        // make sure it's Chrome and not Safari (both webkit-based).
        // setData on DownloadURL returns true on Chrome, and false on Safari
        if (e.dataTransfer && e.dataTransfer.constructor == Clipboard &&
            e.dataTransfer.setData('DownloadURL','http://www.box.net')) {
        var url = (this.dataset && this.dataset.downloadurl) ||
                    this.getAttribute("data-downloadurl");
        e.dataTransfer.setData("DownloadURL", url);
        }
    }, false);
    return;
}
}

Ctrl बटन को चालू करने के अलावा, हमने एक छोटा टॉस्टर टूलटिप भी जोड़ा है. यह तब दिखता है, जब उपयोगकर्ता पेज पर सामान्य तरीके से आइटम को खींचता और छोड़ता है. इससे उपयोगकर्ता को पता चलता है कि Ctrl बटन दबाकर फ़ाइल के आइकॉन को डेस्कटॉप पर खींचकर छोड़ने पर, फ़ाइलें डाउनलोड की जा सकती हैं.

दूसरे चरण की समस्याएं

सुरक्षा से जुड़ी समस्याओं की वजह से, Box.net सीधे स्टैटिक फ़ाइलों को ऐक्सेस करने के लिए, कभी न बदलने वाले यूआरएल नहीं दिखाता. यह सिर्फ़ Box.net के लिए नहीं है. किसी भी ऑनलाइन स्टोरेज सेवा को, ज़्यादा सुरक्षा के बिना हमेशा के लिए उपलब्ध यूआरएल को ज़ाहिर नहीं करना चाहिए. इससे यह पता चलता है कि फ़ाइल सार्वजनिक है या नहीं और डाउनलोड करने का अनुरोध, ज़रूरी अनुमतियों वाले उपयोगकर्ता ने किया है या नहीं.

किसी आइटम के "डाउनलोड यूआरएल" (उदाहरण के लिए, https://www.box.net/box_download_file?file_id=f_60466690) पर जाने पर, यह "302 मिला" स्टेटस कोड दिखाता है. साथ ही, किसी ऐसे यूआरएल (उदाहरण के लिए, https://www.box.net/dl/6045?a=1f1207a084&m=168299,11211&t=2&b=aca15820d924e3b) पर रीडायरेक्ट करता है जो फ़ाइल का अस्थायी "असल यूआरएल" होता है. समस्या यह है कि इसकी समयसीमा हर कुछ मिनट में खत्म हो जाती है. इसलिए, इसे एचटीएमएल आउटपुट में डालना मुमकिन नहीं है. जब उपयोगकर्ता कुछ मिनट पहले जनरेट किए गए एचटीएमएल आउटपुट में मौजूद लिंक से फ़ाइल डाउनलोड करने की कोशिश करता है, तो यह "404" कोड दिखा सकता है.

DnD Download सिर्फ़ उन असली यूआरएल पर काम करता है जो सीधे किसी रिसॉर्स पर ले जाते हैं. अगर रीडायरेक्ट शामिल है, तो फ़िलहाल यह चेन को फ़ॉलो करने के लिए ज़रूरत के मुताबिक स्मार्ट नहीं है (और सुरक्षा की वजह से, इसे कभी भी चेन को फ़ॉलो नहीं करना चाहिए). इसलिए, ऊपर दिया गया लिंक https://www.box.net/box_download_file?file_id=f_60466690, ब्राउज़र के पता बार में डालने पर फ़ाइल डाउनलोड हो जाएगी. हालांकि, यह डीएनडी के साथ काम नहीं करेगा.

"असल यूआरएल" और "रीडायरेक्ट यूआरएल" के बीच के अंतर को बेहतर तरीके से समझने के लिए, ये स्क्रीनशॉट देखें:

302 रीडायरेक्ट यूआरएल
302 रीडायरेक्ट यूआरएल
सही यूआरएल
असल यूआरएल

तीसरा इटरेशन

आइए, Ajax को आज़माते हैं.

हमने पिछले वर्शन में कोड में थोड़ा बदलाव किया है. इससे हमें ये नतीजे मिले हैं:

// DnD to desktop when the Ctrl key is pressed while dragging
if (e.ctrlKey) {
var that = $(e.target);
// make sure it is not IE (attachEvent).
if (that[0].addEventListener) {
that[0].addEventListener("dragstart", function(e) {
    // e.dataTransfer in Firefox uses the DataTransfer constructor
    // instead of Clipboard
    // make sure it's Chrome and not Safari (both webkit-based).
    // setData on DownloadURL returns true on Chrome, and false on Safari
    if (e.dataTransfer && e.dataTransfer.constructor == Clipboard &&
        e.dataTransfer.setData('DownloadURL', 'http://www.box.net')) {
    var url = (this.dataset && this.dataset.downloadurl) ||
                this.getAttribute("data-downloadurl");
    $.ajax({
        complete: function(data) {
        e.dataTransfer.setData("DownloadURL", data.responseText);
        },
        type:'GET',
        url: url
    });
    }
}, false);
return;
}
}

यह सही है. फ़ाइल को खींचने के दौरान, यह सर्वर पर तुरंत Ajax कॉल करता है, ताकि फ़ाइल का नया डाउनलोड यूआरएल वापस पाया जा सके. हालांकि, यह काम नहीं करता.

ऐसा लगता है कि यह सिंक्रोनस कॉल होना चाहिए (या इसे Sjax कहा जा सकता है). ऐसा लगता है कि setData को उस समय लागू करना होगा, जब इवेंट लिसनर अटैच किया गया हो. jQuery के एपीआई के मुताबिक, हाइलाइट की गई लाइनें इस तरह दिखती हैं:

$.ajax({
async: false,
complete: function(data) {
e.dataTransfer.setData("DownloadURL", data.responseText);
},
type: 'GET',
url: url
});

यह तब तक ठीक से काम करता है, जब तक नेटवर्क कनेक्शन को अनप्लग नहीं किया जाता. यह सिंक्रोनस कॉल करता है, इसलिए कॉल पूरा होने तक ब्राउज़र फ़्रीज़ हो जाता है. अगर Ajax कॉल पूरा नहीं होता है (404 कोड मिलता है या कोई जवाब नहीं मिलता), तो ब्राउज़र बिल्कुल भी डिफ़्रॉस्ट नहीं होगा, जैसे कि वह क्रैश हो गया हो.

ऐसा करने से ज़्यादा सुरक्षित रहेगा:

$.ajax({
async: false,
complete: function(data) {
e.dataTransfer.setData("DownloadURL", data.responseText);
},
error: function(xhr) {
if (xhr.status == 404) {
    xhr.abort();
}
},
type: 'GET',
timeout: 3000,
url: url
});

इस सुविधा के डेमो के लिए, Box.net खाते में कोई स्टैटिक फ़ाइल अपलोड करें. Ctrl बटन को दबाकर, फ़ाइल के आइकॉन को खींचकर अपने डेस्कटॉप पर छोड़ें. अगर आपके पास खाता नहीं है, तो इसे बनाने में 30 सेकंड से भी कम समय लगता है.

इस सुविधा की मदद से, क्रिएटिविटी का इस्तेमाल करके कई काम किए जा सकते हैं. किसी इमेज को Windows प्रिंटर डायलॉग में खींचकर छोड़ने पर, इमेज तुरंत प्रिंट हो जाएगी. Box से किसी गाने को अपने मोबाइल फ़ोन के ड्राइव में कॉपी किया जा सकता है. साथ ही, Box से किसी फ़ाइल को खींचकर अपने मैसेज क्लाइंट में छोड़ा जा सकता है, ताकि उसे सीधे अपने दोस्त को ट्रांसफ़र किया जा सके… इससे आपकी उत्पादकता बढ़ाने के अनगिनत अवसर मिलते हैं.

फ़ाइल को प्रिंटर पर भेजना
किसी फ़ाइल को प्रिंटर पर खींचकर छोड़ना.
किसी फ़ाइल को मैसेजिंग क्लाइंट पर खींचकर छोड़ना
किसी फ़ाइल को मैसेज क्लाइंट पर खींचकर छोड़ना.

सुझाव, राय, और आने वाले समय में होने वाले सुधार

हालांकि, यह तरीका भी सही नहीं है, क्योंकि सिंक्रोनस कॉल की वजह से ब्राउज़र कुछ समय के लिए लॉक हो सकता है. एचटीएमएल 5 वेब वर्कर्स से भी मदद नहीं मिलती, क्योंकि वेब वर्कर्स को असाइनोक्रोनस होना चाहिए. ऐसा लगता है कि setData को उस समय किया जाना चाहिए, जब इवेंट लिसनर अटैच किया गया हो.

असल में, परफ़ॉर्मेंस काफ़ी अच्छी है. सिंक्रोनस Ajax (Sjax) कॉल, सिर्फ़ एक यूआरएल स्ट्रिंग को वापस लाता है. यह बहुत तेज़ी से होना चाहिए. हालांकि, एचटीटीपी हेडर में इसका इस्तेमाल करने पर, ज़्यादा ओवरहेड होता है. इसे WebSockets की मदद से ठीक किया जा सकता है. हालांकि, जब तक हमें इस तरह की टेक्नोलॉजी का ज़्यादा इस्तेमाल नहीं दिखता, तब तक क्लाइंट को हर छोटा अपडेट भेजने के लिए, वेबसोकेट का इस्तेमाल करना सही नहीं है.

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

  • कॉलम में खींचें और छोड़ें
  • सूची को फिर से व्यवस्थित करना
  • इमेज गैलरी बनाना
  • कैनवस इमेज एक्सपोर्ट करना

रेफ़रंस