Значение поля total в событиях ProgressEvent. Особенности реализации прогрессбара с помощью ajax
Появилась задача загружать ajax-ом с сервера файлы размером порядка нескольких десятков мегабайт. Чтобы пользователь знал, сколько ему придётся ещё ждать, было принято решение показывать пользователю не обычный лоадер-спиннер, а прогрессбар с процентом выполнения загрузки. Загружаемые файлы хорошо сжимаются gzip-ом, поэтому на сервере в конфиге nginx была включена соответствующая опция. Теперь эти большие файлы стали весить в 3-4 раза меньше, но всё равно занимали ощутимый объём.
Чтобы показывать реальный процент скаченных данных нужно знать общий объём и текущий скаченный объём: event.loaded / event.total
. Количество скаченных байт можно получить прочитав свойство loaded
объекта события ProgressEvent
, а общий объём — свойство total
. Однако при gzip-сжатии в заголовках ответа отсутствует поле lengthComputable
, поскольку сжатие производится в режиме реального времени, т.е. данные начинают отдаваться до окончания полного сжатия файла.
Поэтому для организации прогрессбара требуется до старта загрузки с помощью ajax иметь информацию о размере скачиваемого файла, причём как в обычном виде, так и в gzip-е. Потому что, как оказалось, хотя данные и скачиваются в сжатом виде поле loaded
даёт информацию о количестве скаченных байт в разархивированном виде. Так себя ведут почти все браузеры: Chrome, Opera, Edge. Лишь только Firefox показывает размер в сжатом виде. А если браузер и вовсе не поддерживает gzip, о чём сервер узнаёт по свойству Accept-Encoding
заголовка запроса, то можно использовать свойство lengthComputable
события ProgressEvent
.
Для определения браузера можно использовать, например, библиотеку bowser.
function onProgress(event) {
var total;
if (event.lengthComputable) {
// Размер файла берём из заголовка.
total = event.total;
} else {
// Иначе размер файла берем из мета-данных в зависимости от типа сжатия.
// Все браузеры кроме FF отдают в поле `loaded` количество байт до сжатия,
// FF отдаёт количество байт, реально переданных по сети.
if (event.target.getResponseHeader('Content-Encoding') === 'gzip' && bowser.firefox) {
total = size_gzip;
} else {
total = size;
}
}
if (!total || !event.loaded) return;
console.log(event.loaded / total);
};
var size = 18874368;
var size_gzip = 4718592;
var request = new XMLHttpRequest();
request.upload.addEventListener("progress", onProgress, false);
Добавить комментарий