javascript - jquery get page title




Обнаруживать, когда браузер загружает файлы (11)

Быстрое решение, если вы хотите отображать сообщение или gizer загрузчика до тех пор, пока не появится диалоговое окно загрузки, следует поместить сообщение в скрытый контейнер, и когда вы нажимаете кнопку, которая генерирует загружаемый файл, вы делаете контейнер видимым. Затем используйте jquery или javascript, чтобы поймать событие фокуса кнопки, чтобы скрыть контейнер, содержащий сообщение

У меня есть страница, которая позволяет пользователю загружать динамически сгенерированный файл. Для создания требуется много времени, поэтому я бы хотел показать индикатор ожидания. Проблема в том, что я не могу понять, как определить, когда браузер получил файл, поэтому я могу скрыть индикатор.

Я делаю запрос в скрытой форме, которая отправляется на сервер и нацелена на скрытый iframe для его результатов. Поэтому я не заменяю все окно браузера на результат. Я слушаю событие «load» на iframe, в надежде, что оно будет срабатывать, когда загрузка будет завершена.

Я возвращаю заголовок «Content-Disposition: attachment» с файлом, что заставляет браузер отображать диалог «Сохранить». Но браузер не запускает событие «load» в 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

Это работает в Firefox; он получает пустой HTML-файл, запускает событие «load», затем показывает диалоговое окно «Сохранить» для загружаемого файла. Но он не работает на IE и Safari; IE запускает событие «load», но не загружает файл, и Safari загружает файл (с неправильным именем и типом содержимого) и не запускает событие «load».

Другой подход может заключаться в том, чтобы позвонить, чтобы начать создание файла, а затем опросить сервер, пока он не будет готов, а затем загрузите уже созданный файл. Но я бы предпочел не создавать временные файлы на сервере.

Кто-нибудь имеет лучшую идею?


Вопрос заключается в том, чтобы во время создания файла отображался индикатор ожидания, а затем возвращался к нормальному состоянию после загрузки файла. Как мне нравится делать это, используйте скрытый iFrame и подключайте событие onload к фрейму, чтобы моя страница знала, когда начнется загрузка. НО загрузка не запускается в IE для загрузки файлов (например, с токеном заголовка вложения). Опрос сервера работает, но мне не нравится дополнительная сложность. Итак, вот что я делаю:

  • Задайте скрытый iFrame, как обычно.
  • Создайте контент. Кэшируйте его с абсолютным таймаутом через 2 минуты.
  • Отправьте javascript на перенаправление обратно на вызывающий клиент, по существу, вызывающий страницу генератора во второй раз. ПРИМЕЧАНИЕ. Это приведет к тому, что событие onload будет запущено в IE, потому что оно действует как обычная страница.
  • Удалите содержимое из кеша и отправьте его клиенту.

Отказ от ответственности, не делайте этого на загруженном сайте, из-за того, что кеширование может складываться. Но на самом деле, если ваши сайты, занятые длительным запуском процесса, будут голодать в любом случае.

Вот что выглядит codebehind, и это все, что вам действительно нужно.

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'");
        }
    }

Если вы загрузили файл, который был сохранен, в отличие от того, чтобы быть в документе, невозможно определить, когда загрузка будет завершена, поскольку она не входит в объем текущего документа, а отдельный процесс в браузере.


Если вы не хотите создавать и хранить файл на сервере, готовы ли вы сохранить статус, например, файл в процессе, полный файл? Ваша страница ожидания может опросить сервер, чтобы узнать, когда будет завершено создание файла. Вы не знали бы наверняка, что браузер начал загрузку, но у вас будет определенная уверенность.


Когда пользователь запускает генерацию файла, вы можете просто назначить уникальный идентификатор этой «загрузке» и отправить пользователя на страницу, которая обновляет (или проверяет с помощью AJAX) каждые несколько секунд. Как только файл будет завершен, сохраните его под тем же уникальным идентификатором и ...

  • Если файл готов, выполните загрузку.
  • Если файл не готов, покажите ход.

Затем вы можете пропустить весь беспорядок iframe / waiting / browserwindow, но у вас действительно элегантное решение.


Одно из возможных решений использует JavaScript на клиенте.

Клиентский алгоритм:

  1. Создайте случайный уникальный токен.
  2. Отправьте запрос на загрузку и включите токен в поле GET / POST.
  3. Покажите индикатор ожидания.
  4. Начните таймер, и каждую секунду или около того найдите файл cookie с именем «fileDownloadToken» (или все, что вы решите).
  5. Если файл cookie существует и его значение совпадает с токеном, скройте индикатор ожидания.

Алгоритм сервера:

  1. Ищите поле GET / POST в запросе.
  2. Если он имеет непустое значение, отбросьте файл cookie (например, «fileDownloadToken») и установите его значение в значение маркера.

Исходный код клиента (JavaScript):

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://.com/a/1459794/59087
    // See: http://shiflett.org/blog/2006/mar/server-name-versus-http-host
    // See: http://.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
    );
}

Очень простое (и хромое) однострочное решение - использовать событие window.onblur() чтобы закрыть диалоговое окно загрузки. Конечно, если это занимает слишком много времени, и пользователь решает сделать что-то еще (например, читать электронные письма), диалог загрузки будет закрыт.


Создайте iframe при нажатии кнопки / ссылки и добавьте ее в тело.

                  $('<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);

Я использую следующее, чтобы загрузить blobs и аннулировать URL-адрес объекта после загрузки. он работает в chrome и firefox!

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;
}

после того, как диалоговое окно загрузки файла будет закрыто, окно снова начнет фокусироваться, чтобы инициировать событие фокусировки.


Я написал простой класс JavaScript, который реализует технику, аналогичную той, которая описана в точном answer . Надеюсь, это может быть полезно кому-то здесь. Проект GitHub называется response-monitor.js

По умолчанию в качестве индикатора ожидания используется spin.js но он также предоставляет набор обратных вызовов для реализации пользовательского индикатора.

JQuery поддерживается, но не требуется.

Известные функции

  • Простая интеграция
  • Без зависимостей
  • Плагин JQuery (дополнительно)
  • Интеграция Spin.js (необязательно)
  • Настраиваемые обратные вызовы для мониторинга событий
  • Обрабатывает несколько одновременных запросов
  • Обнаружение ошибок на стороне сервера
  • Тайм-аут
  • Кросс-браузер

Пример использования

HTML

<!-- 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>

Клиент (обычный JavaScript)

//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

Для получения дополнительных примеров проверьте папку examples в репозитории.


старые темы, я знаю ...

но те, которые ведут здесь Google, могут быть заинтересованы в моем решении. это очень просто, но надежно. и позволяет отображать реальные сообщения о проделанной работе (и их можно легко подключить к существующим процессам):

скрипт, который обрабатывает (моя проблема: получение файлов через http и доставка их как zip) записывает статус в сеанс.

статус проверяется и отображается каждую секунду. thats all (ok, его нет, вам нужно позаботиться о большом количестве деталей (например, одновременные загрузки), но это хорошее место для начала ;-)).

страница загрузки:

    <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
    ?>




mime