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

57 комментариев

Страница 2 из 3  
Incode 17.05.2015 22:47
Сомневаюсь, что получится прикрутить сюда прогрессбар, т.к. для расчетов загруженных данных, нужно как минимум получить объем этих данных, а в вашем случае - проходит какое-то время, пока выполняется запрос к БД, обработка результат и т.д. Поэтому значение "total" (в коде evt.total), будет равняться нулю.
Могу предложить упрощенный вариант без прогрессбара: используйте beforeSend (вызывает функцию перед отправкой ajax-запроса) и complete (вызывает функцию после завершения запроса). Посмотрите пример в песочнице. Там я выставил задержку в пару секунд и вы сможете понять, в чем заключается смысл. Ваш код будет выглядеть примерно так:

// #preloader - элемент, сигнализирующий о том, что процесс запроса выполняется
// например, какой-нибудь текст сообщения или gif-картинка прелоадера
var preloader = $('#preloader'); 
$.ajax({
    url: 'data.php',
    type: 'POST',
    dataType: 'json',
    beforeSend: function () {
        preloader.fadeIn();
    },
    complete: function () {
        preloader.fadeOut();
    },
    data: {
        jsonData: {DayOfWeek: DayOfWeek, NameMons: NameMons, EpZoom: EpZoom}
    },
    success: function (res) {
        Hending_flow(res);
    }
});
Гость 16.09.2015 14:40
Круто! То что надо!
Гость 08.11.2015 15:23
Подскажите, как быть с загрузкой очень больших файлов, например 2гб. В этом случае Мозилла, например подвисает, наверное пытается прочитать его целиком? Может можно как-то частями забирать файлы с диска?
Incode 08.11.2015 17:01
как быть с загрузкой очень больших файлов
А вы изменили лимиты: upload_max_filesize и post_max_size? Что-то мне подсказывает, что причина в этом. Если используете Nginx, то там тоже надо поднять значение client_max_body_size.
Гость 08.11.2015 17:15
Я загружал на локальный сервер (Денвер), ограничения большие (8гб), дело не в самом сервере, похоже, а именно в браузере похоже. Он как бы начинает работать, но периодически подвисает. Если загружать такие файлы, скажем, на Яндекс.Диск, то никаких проблем нет, да и компьютер так сильно не гудит, т.е. ресурсов не столько тратится.

Хмм, странно, попробовал еще раз, Мозила аварийно завершилась, но после перезагрузки все ок, загружает 1,5 гб нормально.
Incode 08.11.2015 17:53
Я загружал на локальный сервер (Денвер)
Может быть дело в Денвере... Я им не пользуюсь уже года 3-4 и другим не советую как раз из-за того, что он глючный, убогий, да и проект уже давным-давно "умер".
Гость 14.11.2015 18:18
Подскажите, пожалуйста, все работает отлично, но после загрузки PHP получает названия файлов на русском в транслите, как от этого избавиться, не могу понять в каком месте они преобразуются и кем?
Гость 14.11.2015 18:23
Разобрался, это стандартные фото Windows в EXIF наверное или еще где имеют названия альтернативные :)
Гость 17.11.2015 22:31
Здравствуйте!
Подскажите, пожалуйста, как модифицировать этот скрипт (который для DOWNLOAD), если мне нужно не просто подгрузить большую картинку, а скачать большой файл (и сохранить его)?
Спасибо!
Incode 17.11.2015 23:59
скачать большой файл (и сохранить его)
Честно говоря, я мало вижу в этом смысла. В каждом браузере и так есть индикатор загрузки и дублировать его на на странице - затея никчёмная. На скорую руку набросал пример. В webkit и FireFox работать будет, а в IE прогресс наблюдается, но на загрузку не отдаёт. Сейчас нет времени разбираться почему, да и игра не сто́ит свеч ))
Гость 13.12.2015 22:44
@Гость, у меня и обычные файлы меняют имя на кракозябры. есть вариант в чем дело?
Incode 13.12.2015 23:50
и обычные файлы меняют имя на кракозябры
Пардон, но вы о чем? Вроде бы проблемы с "кракозябрами", тут никто не обсуждал :)
Гость 14.12.2015 23:43
пардон, не так понял. решил проблему. спасибо за скрипт
Гость 19.12.2015 10:05
Спасибо за скрипт. Я новичок в php и javascript, ваш пример оказался наиболее понятным из найденных мной по данному вопросу.
Единственное, у меня возникла одна проблема наверное пустяковая для специалиста, если кому не сложно, подскажите пожалуйста.
В общем у моей формы загрузки указано action="upload.php", в upload.php осуществляется проверка файла (по размеру и т.п.), загрузка на сервер, он же выдает ошибки, либо ссылку на файл по итогам загрузки (открывается страница upload.php).
Проблема в следующем, при добавлении данного прогресс бара upload.php выполняется (файл успешно загружается, либо не загружается), но сама страница upload.php не грузится, т.е. не выдаются предупреждения (например о превышении размера), не загружаются ссылки на файл.
Incode 19.12.2015 10:28
но сама страница upload.php не грузится
Вы загружаете файл с помощью ajax и в этом случае, обычного перехода на страницу, указанную в атрибуте action элемента <form>, не происходит. Возвращайте ответом на ajax-запрос сообщение об успешной или не успешной загрузке и выводите его на экран в этой же странице. Собственно, смысл ajax как раз и состоит в том, чтобы не перезагружать страницу полностью или не делать переход на другую страницу, а выполнять действия в пределах одной страницы.
Гость 19.12.2015 11:13
Понятно, спасибо. С силу своей не опытности думал, что в данном случае ajax отвечает за слежение над процессом загрузки и вывод индикации этого процесса, а сама загрузка происходит как обычно. Теперь хотя бы понятно, что надо думать, как вывести итог на той же странице с его помощью.
Гость 20.12.2015 09:28
Добрый день, подскажите как сделать индикатор загрузки для нескольких файлов. Каждый файл будет загружаться по очереди и у каждого должен быть свой индикатор загрузки. заранее спасибо)
Incode 20.12.2015 14:53
Каждый файл будет загружаться по очереди
Тут бы не помешало чуть больше конкретики. У вас будут несколько полей для файлов или одно, но multiple, по какому событию будет начинаться загрузка и т.д.
nuklon 17.05.2016 12:40
Привет! Подскажи пожалуйста, как сделать чтобы прогрессбар был у каждого загружаемого файла, а не общий? И показывал соответствующий процент конечно. Много ли заморочек, чтобы поменять?
Incode 17.05.2016 22:52
прогрессбар был у каждого загружаемого файла

@nuklon, если речь идёт о том, чтобы в форме было несколько файловых полей и для каждого свой прогрессбар, то по сути нужно файлы загружать поочередно, а не все сразу.
Попробуйте такой вариант. Как будет работать - не знаю, т.к. на тесты у меня сейчас нет времени:
Показать код
Только нужно учитывать то, что это всё-таки будут абсолютно несвязанные между собой запросы.
Гость 11.06.2016 23:08
Классно, спасибо! То, что надо
Гость 20.07.2016 19:21
Привет! Помогите советом, во многих статьях упоминается прогресс загрузки файла на сервер. А как обработать процесс выполнения какой-нибудь функции на сервере?
Я парсю docx файл на сервере, происходит этот построчно и достаточно долго. Хотелось бы мониторить количество пройденных строк, как это можно сделать с помощью jquery и ajax?
Incode 20.07.2016 20:01
как это можно сделать с помощью jquery и ajax?
Я бы сказал, что однозначно никак, но возможно, что способы, пусть и не очень точно, но расчета времени выполнения сценария на сервере найти можно. Однако, это выходит далеко за пределы JS. Один и тот же сценарий, на одной и той же машине, практически всегда будет выполняться с разной скоростью, поэтому нет смысла бороться за вашу затею.
Гость 27.11.2016 00:46
Не работает код((
kulerezzz 02.05.2017 11:32
Супер, все отлично работает. Был похожий код, тоже с вашего сайта. Добавил дополнительно
xhr: function()... 

Теперь есть классная стандартная бутстраповская строка состояния.
Страница 2 из 3  
Ваш комментарий:
X