पता लगाएं कि ब्राउज़र कब फ़ाइल डाउनलोड प्राप्त करता है


487

मेरे पास एक पृष्ठ है जो उपयोगकर्ता को गतिशील रूप से उत्पन्न फ़ाइल डाउनलोड करने की अनुमति देता है। इसे उत्पन्न होने में लंबा समय लगता है, इसलिए मैं "प्रतीक्षा" सूचक दिखाना चाहूंगा। समस्या यह है, मैं यह पता नहीं लगा सकता कि ब्राउज़र को फ़ाइल प्राप्त करने का पता कैसे लगाया जाए, इसलिए मैं संकेतक छिपा सकता हूं।

मैं एक छिपे हुए रूप में अनुरोध कर रहा हूं, जो सर्वर पर POST करता है, और इसके परिणामों के लिए एक छिपे हुए iframe को लक्षित करता है। ऐसा इसलिए है क्योंकि मैं परिणाम के साथ संपूर्ण ब्राउज़र विंडो को प्रतिस्थापित नहीं करता। मैं iframe पर एक "लोड" घटना के लिए सुनता हूं, इस उम्मीद में कि डाउनलोड पूरा होने पर यह आग लग जाएगी।

मैं फ़ाइल के साथ "सामग्री-विवाद: अनुलग्नक" शीर्ष लेख लौटाता हूं, जो ब्राउज़र को "सहेजें" संवाद दिखाने का कारण बनता है। लेकिन ब्राउज़र iframe में "लोड" ईवेंट को फायर नहीं करता है।

एक दृष्टिकोण जो मैंने कोशिश की वह एक बहु-भाग प्रतिक्रिया का उपयोग कर रहा है। तो यह एक खाली HTML फ़ाइल, साथ ही संलग्न डाउनलोड करने योग्य फ़ाइल भेजेगा। उदाहरण के लिए:

Content-type: multipart/x-mixed-replace;boundary="abcde"

--abcde
Content-type: text/html

--abcde
Content-type: application/vnd.fdf
Content-Disposition: attachment; filename=foo.fdf

file-content
--abcde

यह फ़ायरफ़ॉक्स में काम करता है; यह खाली HTML फ़ाइल प्राप्त करता है, "लोड" घटना को फायर करता है, फिर डाउनलोड करने योग्य फ़ाइल के लिए "सहेजें" संवाद दिखाता है। लेकिन यह IE और सफारी पर विफल रहता है; IE "लोड" घटना को आग लगाता है, लेकिन फ़ाइल डाउनलोड नहीं करता है, और सफारी फ़ाइल (गलत नाम और सामग्री-प्रकार के साथ) डाउनलोड करता है, और "लोड" घटना को आग नहीं देता है।

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

क्या किसी के पास बेहतर विचार है?


4
IE का कोई संस्करण मल्टीपार्ट / x-मिश्रित-प्रतिस्थापन का समर्थन नहीं करता है।
एरिकलॉ

धन्यवाद एरिक - यह जानना अच्छा है। मैं उस दृष्टिकोण के साथ और अधिक समय बर्बाद नहीं करूँगा।
जेडब्ल्यू।

केवल विश्वसनीय तरीका सर्वर पुश नोटिफिकेशन (ASP.NET लोगों के लिए सिग्नलआर) लगता है।
dudeNumber4

1
bennadel.com/blog/… - यह एक सरल उपाय है
Mateen

1
@ दोस्त धन्यवाद डूड! यह वास्तव में सरल है
Fai Zal Dong

जवाबों:


451

एक संभव समाधान क्लाइंट पर जावास्क्रिप्ट का उपयोग करता है।

ग्राहक एल्गोरिथ्म:

  1. एक यादृच्छिक अद्वितीय टोकन उत्पन्न करें।
  2. डाउनलोड अनुरोध सबमिट करें, और GET / POST क्षेत्र में टोकन शामिल करें।
  3. "प्रतीक्षा" सूचक दिखाएं।
  4. टाइमर शुरू करें, और हर दूसरे या तो, "fileDownloadToken" नामक एक कुकी की तलाश करें (या जो भी आप तय करते हैं)।
  5. यदि कुकी मौजूद है, और इसका मान टोकन से मेल खाता है, तो "प्रतीक्षा" संकेतक छिपाएं।

सर्वर एल्गोरिथ्म:

  1. अनुरोध में GET / POST फ़ील्ड देखें।
  2. यदि इसका कोई गैर-रिक्त मान है, तो एक कुकी (जैसे "fileDownloadToken") को छोड़ें, और टोकन के मूल्य पर अपना मान सेट करें।

ग्राहक स्रोत कोड (जावास्क्रिप्ट):

function getCookie( name ) {
  var parts = document.cookie.split(name + "=");
  if (parts.length == 2) return parts.pop().split(";").shift();
}

function expireCookie( cName ) {
    document.cookie = 
        encodeURIComponent(cName) + "=deleted; expires=" + new Date( 0 ).toUTCString();
}

function setCursor( docStyle, buttonStyle ) {
    document.getElementById( "doc" ).style.cursor = docStyle;
    document.getElementById( "button-id" ).style.cursor = buttonStyle;
}

function setFormToken() {
    var downloadToken = new Date().getTime();
    document.getElementById( "downloadToken" ).value = downloadToken;
    return downloadToken;
}

var downloadTimer;
var attempts = 30;

// Prevents double-submits by waiting for a cookie from the server.
function blockResubmit() {
    var downloadToken = setFormToken();
    setCursor( "wait", "wait" );

    downloadTimer = window.setInterval( function() {
        var token = getCookie( "downloadToken" );

        if( (token == downloadToken) || (attempts == 0) ) {
            unblockSubmit();
        }

        attempts--;
    }, 1000 );
}

function unblockSubmit() {
  setCursor( "auto", "pointer" );
  window.clearInterval( downloadTimer );
  expireCookie( "downloadToken" );
  attempts = 30;
}

उदाहरण सर्वर कोड (PHP):

$TOKEN = "downloadToken";

// Sets a cookie so that when the download begins the browser can
// unblock the submit button (thus helping to prevent multiple clicks).
// The false parameter allows the cookie to be exposed to JavaScript.
$this->setCookieToken( $TOKEN, $_GET[ $TOKEN ], false );

$result = $this->sendFile();

कहाँ पे:

public function setCookieToken(
    $cookieName, $cookieValue, $httpOnly = true, $secure = false ) {

    // See: http://stackoverflow.com/a/1459794/59087
    // See: http://shiflett.org/blog/2006/mar/server-name-versus-http-host
    // See: http://stackoverflow.com/a/3290474/59087
    setcookie(
        $cookieName,
        $cookieValue,
        2147483647,            // expires January 1, 2038
        "/",                   // your path
        $_SERVER["HTTP_HOST"], // your domain
        $secure,               // Use true over HTTPS
        $httpOnly              // Set true for $AUTH_COOKIE_NAME
    );
}

4
विस्मयकारी विचार, मैंने इस उत्तर के लिए एक बुनियादी ढांचे के रूप में इसका उपयोग किया jQuery / C # के साथ कई फ़ाइलों को डाउनलोड करने के बारे में
ग्रेग

7
दूसरों के लिए एक सिर: यदि document.cookies में downloadToken शामिल नहीं है, तो कुकी पथ की जांच करें। मेरे मामले में, मुझे सर्वर साइड पर "/" का पथ सेट करना था (उदाहरण के लिए Java में कुकी /सेटपाथ ("/"), भले ही पथ डिफ़ॉल्ट रूप से रिक्त था। कुछ समय के लिए मुझे लगा कि मुद्दा विशेष 'लोकलहोस्ट' डोमेन कुकी हैंडलिंग ( stackoverflow.com/questions/1134290/… ) था, लेकिन अंत में यह मुद्दा नहीं था। हो सकता है कि दूसरों के लिए हालांकि इतना पढ़ने लायक हो।
jlpp

2
आपके समाधान के साथ और अधिक गहराई में जाने से पहले, मुझे आश्चर्य है कि क्या यह क्रॉस डोमेन फ़ाइल डाउनलोड अनुरोध के साथ काम करेगा। क्या आपको लगता है कि यह, या कुकीज़ प्रतिबंध इसे समझौता करेगा?
किक्स Jul३

5
शानदार - यह 100 वर्षों में मेरे साथ नहीं हुआ होगा कि आप एक फ़ाइल डाउनलोड के हिस्से के रूप में कुकीज़ को शामिल कर सकते हैं। धन्यवाद!!
फ्रीफॉलर

8
जैसा कि दूसरों ने बताया है, यह समाधान केवल समस्या का हिस्सा है, सर्वर के लिए फ़ाइल समय तैयार करने की प्रतीक्षा कर रहा है। समस्या का दूसरा हिस्सा, जो फ़ाइल के आकार और कनेक्शन की गति के आधार पर काफी हो सकता है, यह है कि वास्तव में क्लाइंट पर पूरी फ़ाइल प्राप्त करने में कितना समय लगता है। और इस समाधान के साथ हल नहीं है।
AsGoodAsItGets 11

27

window.onblur()लोडिंग संवाद को बंद करने के लिए एक बहुत ही सरल (और लंगड़ा) एक लाइन समाधान घटना का उपयोग करना है । बेशक, अगर बहुत लंबा समय लगता है और उपयोगकर्ता कुछ और करने का फैसला करता है (जैसे ईमेल पढ़ना) तो लोडिंग संवाद बंद हो जाएगा।


यह एक सरल दृष्टिकोण है जो एक फ़ाइल डाउनलोड के लिए लोडिंग ओवरले से छुटकारा पाने के लिए आदर्श है, जिसे onbeforeunloadथैंक यू का उपयोग करके ट्रिगर किया गया था ।
wf4

5
यह सभी ब्राउज़रों में काम नहीं करता है (कुछ वर्तमान विंडो को डाउनलोड वर्कफ़्लो के भाग के रूप में / ब्लर नहीं करते हैं, जैसे सफारी, कुछ IE संस्करण इत्यादि)।
hiattp

4
Chrome और अन्य ऐसे ब्राउज़र उन फ़ाइलों को स्वत: डाउनलोड करते हैं जहां यह स्थिति विफल हो जाएगी।
लकी

@ भाग्यशाली जो केवल डिफ़ॉल्ट रूप से है। यह पूरी तरह से संभव है कि क्रोम का एक उपयोगकर्ता निर्दिष्ट करेगा कि डाउनलोड कहाँ सहेजे जाने चाहिए और इसलिए संवाद बॉक्स देखें
ESR

2
बुरा विचार क्योंकि आप tabchange पर धब्बा सक्रिय करते हैं, या खिड़की के बाहर किसी भी कार्रवाई
माइकल

14

पुराना धागा, मुझे पता है ...

लेकिन उन, कि यहाँ नेतृत्व कर रहे हैं गूगल मेरे समाधान में रुचि हो सकती है। यह बहुत सरल है, फिर भी विश्वसनीय है। और यह वास्तविक प्रगति संदेशों को प्रदर्शित करना संभव बनाता है (और इसे मौजूदा प्रक्रियाओं में आसानी से प्लग किया जा सकता है):

वह स्क्रिप्ट जो संसाधित होती है (मेरी समस्या थी: http के माध्यम से फ़ाइलों को पुनर्प्राप्त करना और उन्हें ज़िप के रूप में वितरित करना) सत्र की स्थिति लिखता है।

स्थिति को प्रदूषित किया जाता है और हर सेकंड प्रदर्शित किया जाता है। सभी (ठीक है, यह नहीं है। आपको बहुत सारे विवरणों का ध्यान रखना होगा [जैसे समवर्ती डाउनलोड], लेकिन इसकी शुरुआत करने के लिए एक अच्छी जगह; ;-))।

डाउनलोड पृष्ठ:

    <a href="download.php?id=1" class="download">DOWNLOAD 1</a>
    <a href="download.php?id=2" class="download">DOWNLOAD 2</a>
    ...
    <div id="wait">
    Please wait...
    <div id="statusmessage"></div>
    </div>
    <script>
//this is jquery
    $('a.download').each(function()
       {
        $(this).click(
             function(){
               $('#statusmessage').html('prepare loading...');
               $('#wait').show();
               setTimeout('getstatus()', 1000);
             }
          );
        });
    });
    function getstatus(){
      $.ajax({
          url: "/getstatus.php",
          type: "POST",
          dataType: 'json',
          success: function(data) {
            $('#statusmessage').html(data.message);
            if(data.status=="pending")
              setTimeout('getstatus()', 1000);
            else
              $('#wait').hide();
          }
      });
    }
    </script>

getstatus.php

<?php
session_start();
echo json_encode($_SESSION['downloadstatus']);
?>

download.php

    <?php
    session_start();
    $processing=true;
    while($processing){
      $_SESSION['downloadstatus']=array("status"=>"pending","message"=>"Processing".$someinfo);
      session_write_close();
      $processing=do_what_has_2Bdone();
      session_start();
    }
      $_SESSION['downloadstatus']=array("status"=>"finished","message"=>"Done");
//and spit the generated file to the browser
    ?>

3
लेकिन अगर उपयोगकर्ता के पास कई विंडो या डाउनलोड खुले हैं? यहाँ भी आपको सर्वर पर एक निरर्थक कॉल मिलती है
युकी

3
यदि आपके पास एक उपयोगकर्ता से कई कनेक्शन हैं, तो वे सभी अन्य कनेक्शन समाप्त होने की प्रतीक्षा करेंगे क्योंकि सत्र_स्टार्ट () उपयोगकर्ता के लिए सत्र को लॉक करता है और इसे एक्सेस करने के लिए अन्य सभी प्रक्रियाओं को रोकता है।
होन्जा कुचाय

2
आपको .each()ईवेंट पंजीकरण के लिए उपयोग करने की आवश्यकता नहीं है । बस कहते हैं$('a.download').click()
रॉबिस्रब्रो

कोड को अंदर न निकालें setTimeout('getstatus()', 1000);। सीधे fn का उपयोग करें:setTimeout(getstatus, 1000);
राको सी। बुल्जन

11

मैं निम्न का उपयोग करता है डाउनलोड करने के लिए blobs और डाउनलोड के बाद ऑब्जेक्ट-url को निरस्त करें। यह क्रोम और फ़ायरफ़ॉक्स में काम करता है!

function download(blob){
    var url = URL.createObjectURL(blob);
    console.log('create ' + url);

    window.addEventListener('focus', window_focus, false);
    function window_focus(){
        window.removeEventListener('focus', window_focus, false);                   
        URL.revokeObjectURL(url);
        console.log('revoke ' + url);
    }
    location.href = url;
}

फ़ाइल डाउनलोड संवाद बंद होने के बाद, विंडो को उसका फ़ोकस वापस मिल जाता है, जिससे फ़ोकस इवेंट चालू हो जाता है।


अभी भी खिड़की को स्विच करने और वापस करने का मुद्दा है जो मोडल को छिपाने का कारण होगा।
dudeNumber4

9
क्रोम जैसे ब्राउजर, जो नीचे की ट्रे में डाउनलोड होते हैं, कभी भी विंडो को ब्लर / रीफोकस नहीं करते हैं।
कोलेमन

10

एल्मर के उदाहरण के आधार पर मैंने अपना समाधान तैयार किया है। तत्वों को परिभाषित डाउनलोड वर्ग के साथ क्लिक करने के बाद यह स्क्रीन पर कस्टम संदेश दिखाने की सुविधा देता है। मैंने संदेश को छिपाने के लिए फोकस ट्रिगर का उपयोग किया है ।

जावास्क्रिप्ट

$(function(){$('.download').click(function() { ShowDownloadMessage(); }); })

function ShowDownloadMessage()
{
     $('#message-text').text('your report is creating, please wait...');
     $('#message').show();
     window.addEventListener('focus', HideDownloadMessage, false);
}

function HideDownloadMessage(){
    window.removeEventListener('focus', HideDownloadMessage, false);                   
    $('#message').hide();
}

एचटीएमएल

<div id="message" style="display: none">
    <div id="message-screen-mask" class="ui-widget-overlay ui-front"></div>
    <div id="message-text" class="ui-dialog ui-widget ui-widget-content ui-corner-all ui-front ui-draggable ui-resizable waitmessage">please wait...</div>
</div>

अब आपको डाउनलोड करने के लिए किसी भी तत्व को लागू करना चाहिए:

<a class="download" href="file://www.ocelot.com.pl/prepare-report">Download report</a>

या

<input class="download" type="submit" value="Download" name="actionType">

प्रत्येक डाउनलोड क्लिक के बाद आपको संदेश दिखाई देगा कि आपकी रिपोर्ट बन रही है, कृपया प्रतीक्षा करें ...


2
यदि उपयोगकर्ता खिड़की पर क्लिक करता है तो क्या होगा?
टॉम रोजगेरो

यह वही है जो मैं देख रहा था, बहुत बहुत धन्यवाद !!
सर्जियो

मेरे मामले में छिपाने () को बुलाया नहीं जा रहा है
प्रशांत पिंपले

8

मैंने एक साधारण जावास्क्रिप्ट वर्ग लिखा है जो कि बैलोरियस उत्तर में वर्णित तकनीक के समान तकनीक को लागू करता है । मुझे उम्मीद है कि यह यहां किसी के लिए उपयोगी हो सकता है। GitHub प्रोजेक्ट को response-monitor.js कहा जाता है

डिफ़ॉल्ट रूप से यह वेटिंग इंडिकेटर के रूप में spin.js का उपयोग करता है लेकिन यह कस्टम इंडिकेटर के कार्यान्वयन के लिए कॉलबैक का एक सेट भी प्रदान करता है।

JQuery समर्थित है, लेकिन आवश्यक नहीं है।

उल्लेखनीय विशेषताएं

  • सरल एकीकरण
  • कोई निर्भरता नहीं
  • JQuery प्लग-इन (वैकल्पिक)
  • Spin.js एकीकरण (वैकल्पिक)
  • घटनाओं की निगरानी के लिए विन्यास योग्य कॉलबैक
  • एक साथ कई अनुरोधों को संभालता है
  • सर्वर साइड त्रुटि का पता लगाने
  • टाइमआउट का पता लगाना
  • क्रॉस ब्राउज़र

उदाहरण उपयोग

एचटीएमएल

<!-- the response monitor implementation -->
<script src="response-monitor.js"></script>

<!-- optional JQuery plug-in -->
<script src="response-monitor.jquery.js"></script> 

<a class="my_anchors" href="/report?criteria1=a&criteria2=b#30">Link 1 (Timeout: 30s)</a>
<a class="my_anchors" href="/report?criteria1=b&criteria2=d#10">Link 2 (Timeout: 10s)</a>

<form id="my_form" method="POST">
    <input type="text" name="criteria1">
    <input type="text" name="criteria2">
    <input type="submit" value="Download Report">
</form>

ग्राहक (सादे जावास्क्रिप्ट)

//registering multiple anchors at once
var my_anchors = document.getElementsByClassName('my_anchors');
ResponseMonitor.register(my_anchors); //clicking on the links initiates monitoring

//registering a single form
var my_form = document.getElementById('my_form');
ResponseMonitor.register(my_form); //the submit event will be intercepted and monitored

ग्राहक (JQuery)

$('.my_anchors').ResponseMonitor();
$('#my_form').ResponseMonitor({timeout: 20});

कॉलबैक वाले ग्राहक (JQuery)

//when options are defined, the default spin.js integration is bypassed
var options = {
    onRequest: function(token){
        $('#cookie').html(token);
        $('#outcome').html('');
        $('#duration').html(''); 
    },
    onMonitor: function(countdown){
        $('#duration').html(countdown); 
    },
    onResponse: function(status){
        $('#outcome').html(status==1?'success':'failure');
    },
    onTimeout: function(){
        $('#outcome').html('timeout');
    }
};

//monitor all anchors in the document
$('a').ResponseMonitor(options);

सर्वर (PHP)

$cookiePrefix = 'response-monitor'; //must match the one set on the client options
$tokenValue = $_GET[$cookiePrefix];
$cookieName = $cookiePrefix.'_'.$tokenValue; //ex: response-monitor_1419642741528

//this value is passed to the client through the ResponseMonitor.onResponse callback
$cookieValue = 1; //for ex, "1" can interpret as success and "0" as failure

setcookie(
    $cookieName,
    $cookieValue,
    time()+300,            // expire in 5 minutes
    "/",
    $_SERVER["HTTP_HOST"],
    true,
    false
);

header('Content-Type: text/plain');
header("Content-Disposition: attachment; filename=\"Response.txt\"");

sleep(5); //simulate whatever delays the response
print_r($_REQUEST); //dump the request in the text file

अधिक उदाहरणों के लिए रिपॉजिटरी पर उदाहरण फ़ोल्डर की जाँच करें ।


5

मुझे पार्टी में बहुत देर हो चुकी है लेकिन मैं इसे यहाँ रखूँगा अगर कोई और मेरा समाधान जानना चाहेगा:

मुझे इस सटीक समस्या के साथ एक वास्तविक संघर्ष था लेकिन मैंने iframes का उपयोग करके एक व्यवहार्य समाधान पाया (मुझे पता है, मुझे पता है। यह भयानक है लेकिन यह एक साधारण समस्या के लिए काम करता है जो मेरे पास था)

मेरे पास एक html पृष्ठ था जिसने एक अलग php स्क्रिप्ट लॉन्च की जो फ़ाइल उत्पन्न करती है और फिर उसे डाउनलोड करती है। Html पेज पर, मैंने html हेडर में निम्न jquery का उपयोग किया है (आपको एक jquery लाइब्रेरी भी शामिल करनी होगी):

<script>
    $(function(){
        var iframe = $("<iframe>", {name: 'iframe', id: 'iframe',}).appendTo("body").hide();
        $('#click').on('click', function(){
            $('#iframe').attr('src', 'your_download_script.php');
        });
        $('iframe').load(function(){
            $('#iframe').attr('src', 'your_download_script.php?download=yes'); <!--on first iframe load, run script again but download file instead-->
            $('#iframe').unbind(); <!--unbinds the iframe. Helps prevent against infinite recursion if the script returns valid html (such as echoing out exceptions) -->
        });
    });
</script>

Your_download_script.php पर, निम्नलिखित हैं:

function downloadFile($file_path) {
    if (file_exists($file_path)) {
        header('Content-Description: File Transfer');
        header('Content-Type: text/csv');
        header('Content-Disposition: attachment; filename=' . basename($file_path));
        header('Expires: 0');
        header('Cache-Control: must-revalidate');
        header('Pragma: public');
        header('Content-Length: ' . filesize($file_path));
        ob_clean();
        flush();
        readfile($file_path);
        exit();
    }
}


$_SESSION['your_file'] = path_to_file; //this is just how I chose to store the filepath

if (isset($_REQUEST['download']) && $_REQUEST['download'] == 'yes') {
    downloadFile($_SESSION['your_file']);
} else {
    *execute logic to create the file*
}

इसे तोड़ने के लिए, jquery सबसे पहले एक iframe में आपकी php स्क्रिप्ट लॉन्च करता है। फ़ाइल जनरेट होते ही iframe लोड हो जाता है। फिर jquery ने स्क्रिप्ट को फ़ाइल को डाउनलोड करने के लिए कहने वाले अनुरोध चर के साथ फिर से स्क्रिप्ट लॉन्च की।

कारण यह है कि आप सभी डाउनलोड और फ़ाइल पीढ़ी को एक बार में नहीं कर सकते हैं यह php हैडर () फ़ंक्शन के कारण है। यदि आप हेडर () का उपयोग करते हैं, तो आप स्क्रिप्ट को वेब पेज के अलावा किसी अन्य चीज़ में बदल रहे हैं और jquery कभी भी डाउनलोड स्क्रिप्ट को 'लोड' होने के रूप में नहीं पहचानेगा। मुझे पता है कि यह आवश्यक नहीं है कि जब कोई ब्राउज़र एक फ़ाइल प्राप्त करता है तो पता लगा सकता है लेकिन आपका मुद्दा मेरे जैसा ही है।


5

यदि आप ऐसी फ़ाइल स्ट्रीम कर रहे हैं जिसे आप गतिशील रूप से जनरेट कर रहे हैं, और इसमें एक रियलटाइम सर्वर-से-क्लाइंट मैसेजिंग लाइब्रेरी भी लागू है, तो आप अपने क्लाइंट को आसानी से अलर्ट कर सकते हैं।

सर्वर-टू-क्लाइंट मैसेजिंग लाइब्रेरी मुझे पसंद है और सिफारिश Socket.io (Node.js के माध्यम से) है। आपके सर्वर स्क्रिप्ट के बाद उस फ़ाइल को उत्पन्न करने के लिए किया जाता है जिसे आपकी अंतिम पंक्ति को उस स्क्रिप्ट में डाउनलोड करने के लिए स्ट्रीम किया जा रहा है, जो सॉकेट के लिए एक संदेश भेज सकता है। क्लाइंट को एक अधिसूचना भेजता है। क्लाइंट पर, Socket.io सर्वर से आने वाले संदेशों को सुनता है और आपको उन पर कार्रवाई करने की अनुमति देता है। दूसरों पर इस पद्धति का उपयोग करने का लाभ यह है कि आप स्ट्रीमिंग के बाद एक "सही" फिनिश ईवेंट का पता लगाने में सक्षम हैं।

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

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

सॉकेट.आईओ स्थापित करने और उपयोग करने के लिए अविश्वसनीय रूप से आसान है। और देखें: http://socket.io/


5

"जब ब्राउज़र फ़ाइल डाउनलोड प्राप्त करता है तो कैसे पता लगाएं?"
मुझे उस कॉन्फिगर की समान समस्या का सामना करना पड़ा:
स्ट्रट्स 1.2.9
jquery-1.3.2।
jquery-ui-1.7.1.custom
IE 11
जावा 5


एक कुकी के साथ मेरा समाधान:
- ग्राहक पक्ष:
अपना फॉर्म सबमिट करते समय, अपने पेज को छिपाने के लिए अपने वेटिंग स्पिनर को लोड करने के लिए अपने जावास्क्रिप्ट फ़ंक्शन को कॉल करें।

function loadWaitingSpinner(){
... hide your page and show your spinner ...
}

फिर, एक फ़ंक्शन को कॉल करें जो प्रत्येक 500ms की जांच करेगा कि क्या एक सर्वर से कुकी आ रही है।

function checkCookie(){
    var verif = setInterval(isWaitingCookie,500,verif);
}

यदि कुकी पाई जाती है, तो प्रत्येक 500ms की जांच करना बंद कर दें, कुकी को समाप्त करें और अपने पेज पर वापस आने के लिए अपने फ़ंक्शन को कॉल करें और प्रतीक्षा स्पिनर को हटा दें ( निकालेंWaitingSpinner () )। यदि आप किसी अन्य फ़ाइल को फिर से डाउनलोड करने में सक्षम होना चाहते हैं, तो कुकी को समाप्त करना महत्वपूर्ण है!

function isWaitingCookie(verif){
    var loadState = getCookie("waitingCookie");
    if (loadState == "done"){
        clearInterval(verif);
        document.cookie = "attenteCookie=done; expires=Tue, 31 Dec 1985 21:00:00 UTC;";
        removeWaitingSpinner();
    }
}
    function getCookie(cookieName){
        var name = cookieName + "=";
        var cookies = document.cookie
        var cs = cookies.split(';');
        for (var i = 0; i < cs.length; i++){
            var c = cs[i];
            while(c.charAt(0) == ' ') {
                c = c.substring(1);
            }
            if (c.indexOf(name) == 0){
                return c.substring(name.length, c.length);
            }
        }
        return "";
    }
function removeWaitingSpinner(){
... come back to your page and remove your spinner ...
}

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

Cookie waitCookie = new Cookie("waitingCookie", "done");
response.addCookie(waitCookie);

मैं किसी की मदद करने की उम्मीद करता हूं!


यह पूरी तरह से काम करता है। इस खूबसूरत नमूने के लिए धन्यवाद।
सेदत कुमकु

4

जब उपयोगकर्ता फ़ाइल की पीढ़ी को चालू करता है, तो आप बस उस "डाउनलोड" के लिए एक अद्वितीय आईडी असाइन कर सकते हैं, और उपयोगकर्ता को एक पृष्ठ पर भेज सकते हैं जो हर कुछ सेकंड में ताज़ा (या AJAX के साथ जांचता है) करता है। एक बार फ़ाइल समाप्त होने के बाद, इसे उसी यूनिक आईडी और ... के तहत सेव करें

  • यदि फ़ाइल तैयार है, तो डाउनलोड करें।
  • यदि फ़ाइल तैयार नहीं है, तो प्रगति दिखाएं।

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


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

3

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


2

मैं सिर्फ यह एक ही समस्या थी। मेरा समाधान अस्थायी फ़ाइलों का उपयोग करना था क्योंकि मैं पहले से ही अस्थायी फ़ाइलों का एक समूह उत्पन्न कर रहा था। फार्म के साथ प्रस्तुत किया गया है:

var microBox = {
    show : function(content) {
        $(document.body).append('<div id="microBox_overlay"></div><div id="microBox_window"><div id="microBox_frame"><div id="microBox">' +
        content + '</div></div></div>');
        return $('#microBox_overlay');
    },

    close : function() {
        $('#microBox_overlay').remove();
        $('#microBox_window').remove();
    }
};

$.fn.bgForm = function(content, callback) {
    // Create an iframe as target of form submit
    var id = 'bgForm' + (new Date().getTime());
    var $iframe = $('<iframe id="' + id + '" name="' + id + '" style="display: none;" src="about:blank"></iframe>')
        .appendTo(document.body);
    var $form = this;
    // Submittal to an iframe target prevents page refresh
    $form.attr('target', id);
    // The first load event is called when about:blank is loaded
    $iframe.one('load', function() {
        // Attach listener to load events that occur after successful form submittal
        $iframe.load(function() {
            microBox.close();
            if (typeof(callback) == 'function') {
                var iframe = $iframe[0];
                var doc = iframe.contentWindow.document;
                var data = doc.body.innerHTML;
                callback(data);
            }
        });
    });

    this.submit(function() {
        microBox.show(content);
    });

    return this;
};

$('#myForm').bgForm('Please wait...');

उस स्क्रिप्ट के अंत में जो मेरे पास मौजूद फ़ाइल को उत्पन्न करती है:

header('Refresh: 0;url=fetch.php?token=' . $token);
echo '<html></html>';

यह iframe पर लोड ईवेंट को निकाल दिया जाएगा। फिर प्रतीक्षा संदेश बंद हो जाता है और फिर फ़ाइल डाउनलोड शुरू हो जाएगी। IE7 और फ़ायरफ़ॉक्स पर परीक्षण किया गया।


2

मेरे अनुभव में, इसे संभालने के दो तरीके हैं:

  1. डाउनलोड पर एक अल्पकालिक कुकी सेट करें, और इसके अस्तित्व के लिए जावास्क्रिप्ट की लगातार जांच करें। केवल वास्तविक मुद्दे से कुकी जीवनकाल सही हो रही है - बहुत कम और जेएस इसे याद कर सकता है, बहुत लंबा और यह अन्य डाउनलोड के लिए डाउनलोड स्क्रीन को रद्द कर सकता है। खोज पर कुकी को हटाने के लिए जेएस का उपयोग करना आमतौर पर इसे ठीक करता है।
  2. भ्रूण / XHR का उपयोग करके फ़ाइल डाउनलोड करें। न केवल आपको पता है कि फ़ाइल डाउनलोड खत्म होने पर, यदि आप XHR का उपयोग करते हैं, तो आप प्रगति की घटनाओं का उपयोग कर एक प्रगति बार दिखा सकते हैं! IE / Edge में msSaveBlob और फ़ायरफ़ॉक्स / क्रोम में एक डाउनलोड लिंक ( जैसे यह ) के साथ परिणामी बूँद को सहेजें । इस पद्धति के साथ समस्या यह है कि iOS सफारी सही ढंग से डाउनलोड करने को संभालने के लिए प्रतीत नहीं होता है - आप बूँद को एक फाइलरैडर के साथ डेटा URL में बदल सकते हैं और इसे एक नई विंडो में खोल सकते हैं, लेकिन यह फाइल को खोल रहा है, इसे सहेज नहीं रहा है।

2

अभिवादन, मुझे पता है कि विषय पुराना है, लेकिन मैं एक समाधान छोड़ता हूं जिसे मैंने कहीं और देखा और यह काम किया:

/**
 *  download file, show modal
 *
 * @param uri link
 * @param name file name
 */
function downloadURI(uri, name) {
// <------------------------------------------       Do someting (show loading)
    fetch(uri)
        .then(resp => resp.blob())
        .then(blob => {
            const url = window.URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.style.display = 'none';
            a.href = url;
            // the filename you want
            a.download = name;
            document.body.appendChild(a);
            a.click();
            window.URL.revokeObjectURL(url);
            // <----------------------------------------  Detect here (hide loading)
            alert('File detected'));
        })
        .catch(() => alert('An error sorry'));
}

आप इसका उपयोग कर सकते हैं:

downloadURI("www.linkToFile.com", "file.name");

1

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


8
मुझे स्पष्ट करना चाहिए - मैं डाउनलोड के पूरा होने पर बहुत चिंतित नहीं हूं। यदि मैं डाउनलोड शुरू होने पर सिर्फ पहचान सकता हूं, तो यह पर्याप्त होगा।
JW।

0

सवाल यह है कि फ़ाइल उत्पन्न होने के दौरान 'प्रतीक्षा' सूचक होता है और फ़ाइल डाउनलोड होने के बाद वापस सामान्य हो जाता है। जिस तरह से मुझे यह पसंद है वह एक छिपे हुए iFrame का उपयोग कर रहा है और डाउनलोड शुरू होने पर मेरे पृष्ठ को बताने के लिए फ्रेम के ऑनलोड इवेंट को हुक करता है। लेकिन फ़ाइल लोड (जैसे अटैचमेंट हेडर टोकन के साथ) के लिए BUT ओनलोड IE में फायर नहीं करता है। सर्वर काम करता है, लेकिन मैं अतिरिक्त जटिलता को नापसंद करता हूं। तो यहाँ मैं क्या कर रहा हूँ:

  • हमेशा की तरह छिपे हुए आईफ्रेम को लक्षित करें।
  • सामग्री उत्पन्न करें। इसे 2 मिनट में एक पूर्ण समय के साथ कैश करें।
  • कॉलिंग क्लाइंट को एक जावास्क्रिप्ट रीडायरेक्ट भेजें, अनिवार्य रूप से दूसरी बार जनरेटर पेज को कॉल करना। ध्यान दें: यह IE में आग लगने की घटना का कारण होगा क्योंकि यह एक नियमित पृष्ठ की तरह काम कर रहा है।
  • कैश से सामग्री निकालें और ग्राहक को भेजें।

अस्वीकरण, व्यस्त साइट पर ऐसा न करें, क्योंकि कैशिंग में वृद्धि हो सकती है। लेकिन वास्तव में, यदि आपकी साइटें जो लंबे समय तक चलने वाली प्रक्रिया में व्यस्त हैं, तो आप किसी भी तरह से थ्रेड्स को भुनाएंगी।

यहाँ कोडबाइंड जैसा दिखता है, जो आपको वास्तव में चाहिए।

public partial class Download : System.Web.UI.Page
{
    protected System.Web.UI.HtmlControls.HtmlControl Body;

    protected void Page_Load( object sender, EventArgs e )
    {
        byte[ ] data;
        string reportKey = Session.SessionID + "_Report";

        // Check is this page request to generate the content
        //    or return the content (data query string defined)
        if ( Request.QueryString[ "data" ] != null )
        {
            // Get the data and remove the cache
            data = Cache[ reportKey ] as byte[ ];
            Cache.Remove( reportKey );

            if ( data == null )                    
                // send the user some information
                Response.Write( "Javascript to tell user there was a problem." );                    
            else
            {
                Response.CacheControl = "no-cache";
                Response.AppendHeader( "Pragma", "no-cache" );
                Response.Buffer = true;

                Response.AppendHeader( "content-disposition", "attachment; filename=Report.pdf" );
                Response.AppendHeader( "content-size", data.Length.ToString( ) );
                Response.BinaryWrite( data );
            }
            Response.End();                
        }
        else
        {
            // Generate the data here. I am loading a file just for an example
            using ( System.IO.FileStream stream = new System.IO.FileStream( @"C:\1.pdf", System.IO.FileMode.Open ) )
                using ( System.IO.BinaryReader reader = new System.IO.BinaryReader( stream ) )
                {
                    data = new byte[ reader.BaseStream.Length ];
                    reader.Read( data, 0, data.Length );
                }

            // Store the content for retrieval              
            Cache.Insert( reportKey, data, null, DateTime.Now.AddMinutes( 5 ), TimeSpan.Zero );

            // This is the key bit that tells the frame to reload this page 
            //   and start downloading the content. NOTE: Url has a query string 
            //   value, so that the content isn't generated again.
            Body.Attributes.Add("onload", "window.location = 'binary.aspx?data=t'");
        }
    }

0

एक त्वरित समाधान यदि आप केवल एक संदेश या लोडर gif प्रदर्शित करना चाहते हैं, जब तक कि डाउनलोड संवाद प्रदर्शित नहीं होता है, संदेश को एक छिपे हुए कंटेनर में रखना है और जब आप उस बटन पर क्लिक करते हैं जो फ़ाइल को डाउनलोड करने के लिए उत्पन्न करता है तो आप कंटेनर को दृश्यमान बनाते हैं। फिर संदेश को रखने वाले कंटेनर को छिपाने के लिए बटन के फ़ोकसआउट ईवेंट को पकड़ने के लिए jquery या जावास्क्रिप्ट का उपयोग करें


0

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

var form = document.getElementById("frmDownlaod");
 form.setAttribute("action","downoad/url");
 form.setAttribute("target","downlaod");
 var exportwindow = window.open("", "downlaod", "width=800,height=600,resizable=yes");
 form.submit();

var responseInterval = setInterval(function(){
	var winBody = exportwindow.document.body
	if(winBody.hasChildNodes()) // or 'downoad/url' === exportwindow.document.location.href
	{
		clearInterval(responseInterval);
		// do your work
		// if there is error page configured your application for failed requests, check for those dom elemets 
	}
}, 1000)
//Better if you specify maximun no of intervals


0

यह जावा / स्प्रिंग उदाहरण एक डाउनलोड के अंत का पता लगाता है, जिस बिंदु पर यह "लोड हो रहा है ..." सूचक को छुपाता है।

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

जेएस साइड

function buttonClick() { // Suppose this is the handler for the button that starts
    $("#loadingProgressOverlay").show();  // show loading animation
    startDownloadChecker("loadingProgressOverlay", 120);
    // Here you launch the download URL...
    window.location.href = "myapp.com/myapp/download";
}

// This JS function detects the end of a download.
// It does timed polling for a non-expired Cookie, initially set on the 
// client-side with a default max age of 2 min., 
// but then overridden on the server-side with an *earlier* expiration age 
// (the completion of the server operation) and sent in the response. 
// Either the JS timer detects the expired cookie earlier than 2 min. 
// (coming from the server), or the initial JS-created cookie expires after 2 min. 
function startDownloadChecker(imageId, timeout) {

    var cookieName = "ServerProcessCompleteChecker";  // Name of the cookie which is set and later overridden on the server
    var downloadTimer = 0;  // reference to timer object    

    // The cookie is initially set on the client-side with a specified default timeout age (2 min. in our application)
    // It will be overridden on the server side with a new (earlier) expiration age (the completion of the server operation), 
    // or auto-expire after 2 min.
    setCookie(cookieName, 0, timeout);

    // set timer to check for cookie every second
    downloadTimer = window.setInterval(function () {

        var cookie = getCookie(cookieName);

        // If cookie expired (NOTE: this is equivalent to cookie "doesn't exist"), then clear "Loading..." and stop polling
        if ((typeof cookie === 'undefined')) {
            $("#" + imageId).hide();
            window.clearInterval(downloadTimer);
        }

    }, 1000); // Every second
}

// These are helper JS functions for setting and retrieving a Cookie
function setCookie(name, value, expiresInSeconds) {
    var exdate = new Date();
    exdate.setTime(exdate.getTime() + expiresInSeconds * 1000);
    var c_value = escape(value) + ((expiresInSeconds == null) ? "" : "; expires=" + exdate.toUTCString());
    document.cookie = name + "=" + c_value + '; path=/';
}

function getCookie(name) {
    var parts = document.cookie.split(name + "=");
    if (parts.length == 2 ) {
        return parts.pop().split(";").shift();
    }
}

जावा / स्प्रिंग सर्वर साइड

    @RequestMapping("/download")
    public String download(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //... Some logic for downloading, returning a result ...

        // Create a Cookie that will override the JS-created Max-Age-2min Cookie 
        // with an earlier expiration (same name)
        Cookie myCookie = new Cookie("ServerProcessCompleteChecker", "-1");
        myCookie.setMaxAge(0); // this is immediate expiration, 
                               // but can also add +3 sec. for any flushing concerns
        myCookie.setPath("/");
        response.addCookie(myCookie);
        //... -- presumably the download is writing to the Output Stream...
        return null;
}

कुकी JS स्क्रिप्ट द्वारा बनाई गई है, लेकिन यह कंट्रोलर द्वारा अपडेट नहीं की गई है, यह मूल मान (0) को बनाए रखती है, मैं पेज को रिफ्रेश किए बिना कुकी वैल्यू को कैसे अपडेट कर सकता हूं?
शसुकी

यह अजीब है - क्या आप सुनिश्चित कर सकते हैं कि नाम बिल्कुल सही है? यदि नाम से मेल खाता है तो यह कुकी को अधिलेखित कर देगा। मुझे बताएं
जीन बी।

मूल मान 0. नहीं है। जेएस में निर्धारित मूल मान 2 मिनट है। नई मूल्य है कि सर्वर के साथ संशोधित करने के लिए माना जाता है 0. है
जीन बी।

इसके अलावा, क्या आप ऐसा कर रहे हैं: myCookie.setPath("/"); response.addCookie(myCookie);
जीन बी।

मुझे पता चला (किसी कारण के लिए), कि मैं प्रतिक्रिया करने से पहले कुकीज़ जोड़ दूं ।getOutputStream (); (डाउनलोड फ़ाइलों को जोड़ने के लिए प्रतिक्रिया उत्पादन स्ट्रीम प्राप्त करना), यह उस कदम के बाद मैंने इसे ध्यान में नहीं रखा था
शेसुकी

0

प्रधानमंत्री कुकी मतदान का भी उपयोग करते हैं

https://github.com/primefaces/primefaces/blob/32bb00299d00e50b2cba430638468a4145f4edb0/src/main/resources/META-INF/resources/primefaces/core/core.js#L458

    monitorDownload: function(start, complete, monitorKey) {
        if(this.cookiesEnabled()) {
            if(start) {
                start();
            }

            var cookieName = monitorKey ? 'primefaces.download_' + monitorKey : 'primefaces.download';
            window.downloadMonitor = setInterval(function() {
                var downloadComplete = PrimeFaces.getCookie(cookieName);

                if(downloadComplete === 'true') {
                    if(complete) {
                        complete();
                    }
                    clearInterval(window.downloadMonitor);
                    PrimeFaces.setCookie(cookieName, null);
                }
            }, 1000);
        }
    },

-2

बटन / लिंक पर क्लिक करने पर एक आइफ्रेम बनाएं और इसे बॉडी में जोड़ें।

                  $('<iframe />')
                 .attr('src', url)
                 .attr('id','iframe_download_report')
                 .hide()
                 .appendTo('body'); 

देरी के साथ एक iframe बनाएं और डाउनलोड करने के बाद इसे हटा दें।

                            var triggerDelay =   100;
                            var cleaningDelay =  20000;
                            var that = this;
                            setTimeout(function() {
                                var frame = $('<iframe style="width:1px; height:1px;" class="multi-download-frame"></iframe>');
                                frame.attr('src', url+"?"+ "Content-Disposition: attachment ; filename="+that.model.get('fileName'));
                                $(ev.target).after(frame);
                                setTimeout(function() {
                                    frame.remove();
                                }, cleaningDelay);
                            }, triggerDelay);

यह जानकारी का अभाव है और यह "जब लोड को छिपाने के लिए" समस्या को हल नहीं करता है।
टॉम रोजगारो
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.