Ajax на практике.

Progress Bar - индикатор процесса загрузки.

Серия статей "Ajax на практике"

В предыдущей статье, мы коснулись темы загрузки файлов методом $.ajax() и, если обычные данные грузятся и обрабатываются на сервере достаточно быстро, то с файлами этот процесс, может занять значительно больше времени, а тем более, если выполняется мультизагрузка. В первом случае, вполне достаточно вывести для пользователя какой-нибудь анимированный прелоадер, чтоб дать ему понять, что сервер не ушёл на обед, а занимается делом. Во втором же случае, гораздо разумней показать пользователю ход выполнения операции, т.к. веся́щий секунд 5 и больше прелоадер, может утомить юзера или, того хуже, если он подумает, что сервер завис, развернется и уйдет с вашего ресурса. Вот тут-то и пригодится нам "индикатор процесса", он же "прогресс-бар", он же "progress bar" и т.п. ;)

С частью html - всё достаточно просто, хотя и не так безоблачно. Для прогрессбара используют или тег <progress> (реже <meter>), или создают кастомный из любого понравившегося html-элемента. Первые два не очень кроссбраузерны, но предусмотренный в них альтернативный текст, который можно использовать для показа процентов выполнения загрузки, немного исправляет положение. Со вторым вариантом - приходится повозится в CSS, но это не проблема для людей знающих. Мы остановимся на первом, т.к. у нас сейчас основная цель - разобраться с механизмом на JavaScript.

Итак, создадим простую форму для загрузки файла и после поля файла, разместим прогресс-бар, в котором нас интересуют два атрибута: value - текущее значение и max - максимальное значение. Так как мы будем использовать проценты, значения будут колебатся от нуля до ста. Соответственно стартовое значение (value) должно быть "0", а максимальное (max) - "100" :

HTML (файл index.html)

<form action="handler.php" method="post" id="my_form" enctype="multipart/form-data">
  <p>
    <label for="my_file">Файл:</label>
      <input type="file" name="my_file" id="my_file">
        <progress id="progressbar" value="0" max="100"></progress>
  </p>
  <input type="submit" id="submit" value="Отправить">
</form>

В JS пишем практически всё, что разбирали в статье по "загрузке файлов", но добавляем параметр xhr, в callback-функции которого, мы можем работать с объектом XMLHttpRequest, изменяя или дополняя его поведение, получать доступ его свойствам и методам, устанавливать свои обработчики событий и т.д.:

jQuery (файл script.js)

$(function(){
  var progressBar = $('#progressbar');
  $('#my_form').on('submit', function(e){
    e.preventDefault();
    var $that = $(this),
        formData = new FormData($that.get(0));
    $.ajax({
      url: $that.attr('action'),
      type: $that.attr('method'),
      contentType: false,
      processData: false,
      data: formData,
      dataType: 'json',
      xhr: function(){
        var xhr = $.ajaxSettings.xhr(); // получаем объект XMLHttpRequest
        xhr.upload.addEventListener('progress', function(evt){ // добавляем обработчик события progress (onprogress)
          if(evt.lengthComputable) { // если известно количество байт
            // высчитываем процент загруженного
            var percentComplete = Math.ceil(evt.loaded / evt.total * 100);
            // устанавливаем значение в атрибут value тега <progress>
            // и это же значение альтернативным текстом для браузеров, не поддерживающих <progress>
            progressBar.val(percentComplete).text('Загружено ' + percentComplete + '%');
          }
        }, false);
        return xhr;
      },
      success: function(json){
        if(json){
      	  $that.after(json);
        }
      }
    });
  });
});

Что касается серверной части, то там работаем с файлом/файлами, как если бы вы их загружали обычным способом.
Готово! Теперь, чтобы протестировать на локальной машине, нужно выбрать файл побольше, иначе загрузка будет происходить практически моментально и работу прогрессбара, вы попросту не заметите. Что будет в обработчике в этом случае - не важно, главное, чтоб он был. И не забудьте перед тестированием, установить значение директивы upload_max_filesize чуть больше, чем загружаемый файл. Для времменого изменения - это проще всего сделать в файле .htaccess, прописав строку:

php_value upload_max_filesize 1000M # вместо 1000 - своё значение

P.S. На всякий случай, если кому-то понадобится сделать прогресс бар не на UPLOAD, а на DOWNLOAD то нужно внести маленькое изменение, а именно - установить соответствующий обработчик. Ну, естественно, что убрать лишнее. Такое может вам пригодиться, если, например, вы подгружаете на страницу большое изображение.

$(function(){
  var progressBar = $('#progressbar'),
      bigImg = '/image.png'; // путь к очень большой картинке
  // В примере - по клику на какой-то кнопке
  $('#get_big_file').on('click', function(e){
    e.preventDefault();
    $.ajax({
      url: bigImg,
      type: 'GET', // можно и POST
      contentType: false,
      processData: false,
      xhr: function(){
        var xhr = $.ajaxSettings.xhr();
        // Устанавливаем обработчик подгрузки
        xhr.addEventListener('progress', function(evt){
          if (evt.lengthComputable) {
            var percentComplete = Math.ceil(evt.loaded / evt.total * 100);
            progressBar.val(percentComplete);
          }
        }, false);
        return xhr;
      },
      success: function(json){
        // После того, как изображение полностью получено
        // подставляем его URL и выводим на экран
        if(json){
          $('#output').html('<img src="' + bigImg + '">');
        }
      }
    });
  });
});

Пробежавшись по всем четырем статьям, я надеюсь, что у вас уже не возникнет особых сложностей с тем, как работать с методом $.ajax() и вы без труда сможете внедрить его в свои проекты. И наконец, в завершение темы "Ajax", в следующей статейке, разберем реализацию ajax-запросов на чистом (нативном) JavaScript.

Incode Pro logo

54 комментария

Страница 1 из 3  
Incode 20.07.2016 20:01
как это можно сделать с помощью jquery и ajax?
Я бы сказал, что однозначно никак, но возможно, что способы, пусть и не очень точно, но расчета времени выполнения сценария на сервере найти можно. Однако, это выходит далеко за пределы JS. Один и тот же сценарий, на одной и той же машине, практически всегда будет выполняться с разной скоростью, поэтому нет смысла бороться за вашу затею.
Гость 27.11.2016 00:46
Не работает код((
kulerezzz 02.05.2017 11:32
Супер, все отлично работает. Был похожий код, тоже с вашего сайта. Добавил дополнительно
xhr: function()... 

Теперь есть классная стандартная бутстраповская строка состояния.
Гость 03.07.2017 13:27
Здравствуйте, у меня прогрессметр инкрементирует даже если, к примеру адрес принимающего скрипта неверный. Подскажите, как запретить отработку прогрессметра, если поймана ошибка? Спасибо!
Incode 03.07.2017 14:49
как запретить отработку прогрессметра
В методе $.ajax() есть опция error (возможно, что вы знаете). Неверный путь вызовет именно метод этой опции. Я бы попробовал там сбросить прогрессбар на ноль.
$.ajax({
    /* другие опции */
    error: function() {
        progressBar.val(0);
    }
});
Гость 03.07.2017 15:39
Да, знаю, но у меня схема такая, что при нажатии на кнопку аплоада прогресс все таки бежит, несмотря на то, что по факту урл неверный, и только добежав до конца включается обработчик error

        function start(attempts)
        {
            var myXhr = $.ajaxSettings.xhr();
            
            return $.ajax(
            {
                type: "POST",
                url: "/ajax/upload1.php",
                data: formData,
                processData: false,
                contentType: false,
                xhr: function() 
                {
                    if(myXhr.upload)
                    { 
                        myXhr.upload.addEventListener('progress', function(e) 
                        {
                            if(e.lengthComputable)
                            {
                                procent = (e.loaded / e.total) * 100;
                                $("#ProgressBar").css('width',  procent + '%');
                                $("#Procent").html( Math.round(procent) + '%');
                            }
                                                
                        }, false); 
                    }
                            
                    return myXhr;
                },
                success: function(data, textStatus)
                {
                    alert('Все отл')
                },
                error: function(xhr, status) 
                {
                    attempts--;
                // все, попытки кончились
                    if(attempts == 0) 
                    {
                    // Бросаем это дело.
                        alert('Не смог передать')                          
                        return;
                    }
                // еще пытаемся                
                    else
                    {
                        start(attempts);
                    }
                }                                
            });            
        }
Incode 03.07.2017 19:47
но у меня схема такая
Попробуйте ловить прекращение запроса (abort):
myXhr.addEventListener("abort", function(){
    // финалим
});
sash 13.07.2017 18:17
Благодарю, я и не думал что это настолько просто.. Да, это дело ещё можно стилизовать как угодно оказывается)

::-webkit-progress-bar {
  background: white;
}
::-webkit-progress-value {
  background: #3a9289;
}
::-moz-progress-bar {
  background: #3a9289;
}
progress {
  color: #3a9289;
  background: white;
  border: 2px solid #3a9289;
  border-radius: 5px;
}
Гость 05.09.2017 01:41
Здравствуйте а можно на этот пример incode.pro только чтобы скачивание файла происходило по внешней ссылке а не со своего сервера
Incode 05.09.2017 02:35
по внешней ссылке
Это можно сделать при одном условии - если на удаленном ресурсе вам дадут доступ (CORS). Например, владельцы ресурса могут в htaccess прописать доступ всем:
Header set Access-Control-Allow-Origin "*"
или определенному домену(ам)
Header set Access-Control-Allow-Origin "site.com"
или же отправлять соответствующий заголовок
<?php
header("Access-Control-Allow-Origin: http://site.com");
Без такого доступа, вариантов скачивания на клиенте - нет.
Гость 05.09.2017 14:06
Спасибо за ответ общаюсь с тех поддержкой файлообменника fex.net посмотрим что скажут.
Гость 05.09.2017 15:18
Ответили быстро :)
4.1.4 не использовать Ресурс в качестве источника Файлов для Файлообменных Ресурсов и/или сервисов.
4.1.5 не использовать Ресурс в коммерческих целях.
Incode 05.09.2017 15:30
Ответили быстро
Это какие-то их условия, а по сути? Есть возможность доступа к файлам с клиента? Когда они были еще ex.ua и не было никаких ключей доступа, то даже тогда из JS к файлам добраться было невозможно.
Гость 05.09.2017 16:51
Да есть возможность доступа файлам с клиента без всяких ключей. Но думаю есть ограничение по трафику но пока молчат, посмотрим. Главное чтобы пока скорость не падала меньше 2 Mb/s
Гость 05.09.2017 17:48
с Ftp у них нет доступа к файлам конечно
Гость 05.09.2017 18:02
А можно этот пример incode.pro в архиве winrar получить как он есть у вас на сервере. Вроде скопировал код но что то не работает у меня. pdf файл в корне со скриптом ? И так итак пробовал что то не скачивается файл.
Incode 05.09.2017 19:10
А можно этот пример incode.pro в архиве winrar получить
Можно, забирайте
Гость 05.09.2017 19:54
Просто file.pdf у вас под другим названием отдавался с вашего сервера я его просто кидал в корень туда не обратил внимание на это. Спасибо.
Гость 10.09.2017 16:42
Приветствую. Как прикрутить данный скрипт к к бустраповской строке состояния кто то выше писал что про xhr: function()... не понял если можно в примере.
Гость 15.09.2017 09:11
https://incode.pro/examples/progressbar_demo.zip
Данным скриптом пробую скачивать файл 1 гигабайт. Браузер пишет переполнена память и отменяет загрузку. Что нибудь можно сделать чтобы большой файл скачивался.
Гость 16.08.2018 16:01
При нажатии на кнопку без всякого файла в форме выводиться это:

Данные загруженного файла:
Array
(
    [name] => 
    [type] => 
    [tmp_name] => 
    [error] => 4
    [size] => 0
)


Как вместо этого выполнить свой код? (Отменить прогрессбар и вывести текст об ошибке)

Весь код:

$(function(){
	var progressBar = $('progress');
	$('#my_form').on('submit', function(e){
		e.preventDefault();
		var $that = $(this),
            formData = new FormData($that.get(0));
		$.ajax({
			url: $that.attr('action'),
			type: $that.attr('method'),
			contentType: false,
			processData: false,
			data: formData,
			dataType: 'json',
			xhr: function(){
				var xhr = $.ajaxSettings.xhr();
				xhr.upload.addEventListener('progress', function(evt){
					if (evt.lengthComputable) {
						var percentComplete = Math.ceil(evt.loaded / evt.total * 100);
                        $('#example5') 
                            .progress({
                                value    : percentComplete,
                                total    : 100,
                            })
                        ;
						progressBar.val(percentComplete).text(percentComplete + '%');
					}
				}, false);
				return xhr;
			},
			success: function(json){
				if(json){
					$that.after(json);
				}
			},
            error: function() {
                progressBar.val(0);
            }
		});
	});
});


Спасибо!
Гость 01.09.2018 14:19
Мужики, пробуйте хостинг на сверхскоростных SSD. Хостинг находится тут.
Гость 02.12.2018 02:44
Курсов и тренингов для IT-специалистов много, и выбор среди них сделать непросто. Крупнейший каталог тренингов, семинаров и конференций в России.
Гость 02.12.2018 13:15
Выдает ошибку: ReferenceError: progressBar is not defined
браузер FF Quantum
Гость 02.12.2018 13:25
@Гость, разобрался)
не увидел строчку
var progressBar = $('#progressbar');
Страница 1 из 3  
Ваш комментарий:
X