статус код об успешной загрузке файла с javascript
XMLHttpRequest: индикация прогресса
Материал на этой странице устарел, поэтому скрыт из оглавления сайта.
Более новая информация по этой теме находится на странице https://learn.javascript.ru/xmlhttprequest.
Запрос XMLHttpRequest состоит из двух фаз:
Далее – обо всём по порядку.
Стадия отправки
Вот полный список событий:
Пример установки обработчиков на стадию отправки:
Стадия скачивания
После того, как загрузка завершена, и сервер соизволит ответить на запрос, XMLHttpRequest начнёт скачивание ответа сервера.
Все события, возникающие в этих обработчиках, имеют тип ProgressEvent, то есть имеют свойства loaded – количество уже пересланных данных в байтах и total – общее количество данных.
Демо: загрузка файла с индикатором прогресса
Современный XMLHttpRequest позволяет отправить на сервер всё, что угодно. Текст, файл, форму.
Мы, для примера, рассмотрим загрузку файла с индикацией прогресса. Это требует от браузера поддержки File API, то есть исключает IE9-.
Форма для выбора файла с обработчиком submit :
Мы получаем файл из формы через свойство files элемента и передаём его в функцию upload :
Полный пример индикации прогресса при загрузке, основанный на коде выше:
Событие onprogress в деталях
При обработке события onprogress есть ряд важных тонкостей.
Можно, конечно, их игнорировать, но лучше бы знать.
Оно представляет собой объект типа ProgressEvent со свойствами:
Сколько байт уже переслано.
Имеется в виду только тело запроса, заголовки не учитываются.
Общее количество байт для пересылки, если известно.
А может ли оно быть неизвестно?
Ещё особенности, которые необходимо учитывать при использовании onprogress :
Событие происходит при каждом полученном/отправленном байте, но не чаще чем раз в 50 мс.
Можно до окончания запроса заглянуть в него и прочитать текущие полученные данные. Важно, что при пересылке строки в кодировке UTF-8 кириллические символы, как, впрочем, и многие другие, кодируются 2 байтами. Возможно, что в конце одного пакета данных окажется первая половинка символа, а в начале следующего – вторая. Поэтому полагаться на то, что до окончания запроса в responseText находится корректная строка нельзя. Она может быть обрезана посередине символа.
Исключение – заведомо однобайтные символы, например цифры или латиница.
Сработавшее событие xhr.upload.onprogress не гарантирует, что данные дошли.
Событие xhr.upload.onprogress срабатывает, когда данные отправлены браузером. Но оно не гарантирует, что сервер получил, обработал и записал данные на диск. Он говорит лишь о самом факте отправки.
Поэтому прогресс-индикатор, получаемый при его помощи, носит приблизительный и оптимистичный характер.
Файлы и формы
Выше мы использовали xhr.send(file) для передачи файла непосредственно в теле запроса.
При этом посылается только содержимое файла.
Если нужно дополнительно передать имя файла или что-то ещё – это можно удобно сделать через форму, при помощи объекта FormData:
Создадим форму formData и прибавим к ней поле с файлом file и именем «myfile» :
Возобновляемая загрузка файлов
При помощи fetch достаточно просто отправить файл на сервер.
Но как возобновить загрузку, если соединение прервалось? Для этого нет готовой настройки, но у нас есть все средства, чтобы решить эту задачу самостоятельно.
Возобновляемая загрузка должна сопровождаться индикацией прогресса, ведь, скорее всего, нам нужно отправлять большие файлы. Поскольку fetch не позволяет отслеживать прогресс отправки, то мы будем использовать XMLHttpRequest.
Не очень полезное событие progress
Чтобы возобновить отправку, нам нужно знать, какая часть файла была успешно передана до того, как соединение прервалось.
Возможно, отправленные данные оказались в буфере прокси-сервера локальной сети или удалённый сервер просто отключился и не смог принять их, или данные потерялись где-то по пути при разрыве соединения и так и не достигли пункта назначения.
В общем, событие progress подходит только для того, чтобы показывать красивый индикатор загрузки, не более.
Для возобновления же загрузки нужно точно знать, сколько байт было получено сервером. И только сам сервер может это сказать, поэтому будем делать для этого отдельный запрос.
Алгоритм
Во-первых, создадим уникальный идентификатор для файла, который собираемся загружать:
Это нужно, чтобы при возобновлении загрузки серверу было понятно, какой файл мы продолжаем загружать.
Далее, посылаем запрос к серверу с просьбой указать количество уже полученных байтов:
Ниже представлен демо-код как для сервера (Node.js), так и для клиента.
Пример работает только частично на этом сайте, так как Node.js здесь располагается за другим веб-сервером Nginx, который сохраняет в своём буфере все загружаемые файлы и передаёт их дальше в Node.js только после завершения загрузки.
Но вы можете скачать код и запустить его локально, чтобы увидеть полный пример в действии:
Загрузка JavaScript-файлов. Решаем проблему Ctrl-F5
Все мы знаем сотню способов загрузки скриптов. У каждого свои плюсы и минусы.
Хочу представить вам очередной метод загрузки js-файлов. Я также понимаю, что такой метод активно используется в сети, но статей про него я не видел.
Поэтому опишу способ, которым пользуюсь сам, в надежде, что он вам тоже понравится.
Цели: модульность разработки, быстрота загрузки, валидный кэш.
Бонус: индикатор загрузки
UPD. Обозначил главную цель этого метода — валидный кэш.
При использовании данного метода, у вас не будет неуверенности в том, обновится ли скрипт и будет ли он работать у конечного пользователя.
UPD 2. Для тех кто не дочитывает до конца (я вас прекрасно понимаю), в концовке сказано, как всё можно сделать намного проще.
Вместо core.633675510761.js писать core.js?v=633675510761. И там же указано, почему всё же написано так много.
UPD 3. В комментариях от david_mz, WebByte прозвучало предложение для обработки запроса использовать не JSHandler, а urlrewrite.
Под модульностью я понимаю, что каждый компонент системы расположен в отдельном файле: core.js, utils.js, control.js, button.js и т.д.
От этого принципа я не отказываюсь даже при загрузке страницы. Хотя я знаю, что загрузка 1 файла 100Кб быстрее 10-ти по 10Кб.
Эту проблему я решу через кэширование далее.
Помимо использования заголовков «If-Modified-Since» и «If-None-Match» (ETag) я устанавливаю Expires через год!
Теперь почему я так смело поступаю и уверен что мой файл будет год валидным.
Потому что я приписываю к имени файла дату его последней модификации!
Т.е. есть core.js, включение происходит так
Для наглядности приведу пару скриншотов. Интернет очень медленный.
Первое обращение к странице.
Второе:
Изменяем один файл — третье обращение
Как видите из второго и третьего рисунков, браузер обновил изменённый скрипт. Также видно, что он не удосужился проверить все файлы на наличие изменений. Т.е. на странице много картинок, а он почему-то проверил только две. Тоже самое происходит со скриптами. Они обновляются не всегда. Web-сервер может устанавить дополнительные заголовки для статических файлов, вроде Set-Expires + (1-9999) минут. Плюс внутреняя логика браузера и proxy-серверов. Вообщем, на что мы повлияеть не можем.
Это была теория. Реализовать на практике это не составляет никакого труда.
Я приведу пример как я решаю это на ASP.NET. Поэтапно.
1. Для включения файлов на страницу я использую специальный объект, который проверяет на уникальность включаемого файла. А затем при рендинге пишет мои файлы с префиком даты.
public class ScriptHelper
<
protected StringCollection includeScripts = new StringCollection();
Комментарий:
prefix — префикс относительного пути папки со скриптами
FileSystemWatcherManager — менеджер по работе с физическими файлами. Этот класс позволяет избегать частых вызовов System.IO.File.GetLastWriteTimeUtc(), и является простой оболочкой монитора файловой системы. Позволю себе привести полный код.
using System;
using System.IO;
using System.Collections. Generic ;
foreach ( String pattern in filter.Split( ‘,’ ) )
<
FileSystemWatcher dirWatcher = new FileSystemWatcher( directory, pattern );
dirWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite;
dirWatcher.IncludeSubdirectories = true ;
dirWatcher.EnableRaisingEvents = true ;
dirWatcher.Changed += new FileSystemEventHandler( OnFileSystemChanged );
dirWatcher.Created += new FileSystemEventHandler( OnFileSystemChanged );
dirWatcher.Renamed += new RenamedEventHandler( OnFileSystemRenamed );
UpdateLastModifiedFiles( directory, pattern, true );
>
>
private static void OnFileSystemRenamed( object sender, RenamedEventArgs e )
<
UpdateLastModifiedFiles( Path.GetDirectoryName( e.FullPath ), ( (FileSystemWatcher)sender ).Filter, true );
>
private static void OnFileSystemChanged( object sender, FileSystemEventArgs e )
<
UpdateLastModifiedFiles( Path.GetDirectoryName( e.FullPath ), ((FileSystemWatcher)sender).Filter, true );
>
Вызов в global.asax
Думаю, комментарии излишни, единственное отмечу, под DEBUG режимом, я использую реальные имена файлов, чтобы дебаггер мог их цеплять.
Следующий пункт, это обработчик js-файлов.
Включается через web.config
Обработчик нужен чтобы удалить префикс со временем изменения и отдать реальный файл. Также он проверяет заголовки If-None-Match, If-Modified-Since, устанавливает LastModified, ETag и Expires. Также возможен выбор файла, оригинальный, минимизированый, сжатый, проверка прав и прочее.
Привожу облечённую версию.
if ( context.Request.HttpMethod == «GET» )
<
context.Response.TransmitFile( file );
>
context.Response.StatusCode = 200;
context.Response.StatusDescription = «OK» ;
>
>
>
catch ( Exception ex )
<
WL.Logger.Instance.Error( ex );
context.Response.StatusCode = 500;
context.Response.StatusDescription = «Internal Server Error» ;
>
>
Вместо концовки
Возможно вы скажете, много сложностей с реализацией отдельного хэндлера.
Могу посоветовать более простой способ обработки файла. Вместо писать
Тогда не нужен JSHandler. Но я не уверен как будет работать кэш. Фактически имя файла не меняется, а появляется только дополнительный параметр. Т.е. может возникнуть та же проблема с Ctrl-F5 из-за внутренного кэша браузера или прокси-сервера.
Но мне отдельный JSHandler нужен ещё для проверки прав на доступ к скриптам, например из папки Admin отдаю только админам.
Плюс ко всему кому-то будет полезно и интересно посмотреть реализации JSHander’а и монитора файловой системы.
Если задача JSHandler только в отрезании ключа модификации, то его можно заменить urlrewrite-модулем.
И обещанный бонус с индикатором загрузки.
Пока грузятся ваши скрипты первый раз, визуально ускорить процесс можно показав процесс загрузки.
Помните место где я пишу включение файлов? Там на самом деле код такой:
StringBuilder clientScript = new StringBuilder ();
if ( includeScripts.Count > 0 )
<
clientScript.Append( @»
Рисуется два div’а. И второму по мере загрузки наращивается ширина.
В CSS это выглядит вот так:
Улучшение скорости загрузки сайта. Часть 2. Оптимизация CSS и JavaScript файлов
Вторым шагом после изображений подумайте об оптимизации ваших CSS и JavaScript файлов. Не забывайте всегда минимизировать эти файлы. По умолчанию CSS являются ресурсом, который блокирует отрисовку страницы в браузере. Это значит, что браузер остановит создание HTML элементов пока не загрузит и не прочтет весь ваш CSS. Поэтому всегда минимизируйте CSS, а также удалите неиспользуемые стили. Это особенно важно если Вы используете какой-нибудь CSS фреймворк (на вряд ли вы используете все стили фреймворка). PurgeCSS может помочь Вам автоматизировать процесс удаления неиспользуемых стилей. Если это возможно для Вас, постарайтесь при загрузке страницы загружать только критический CSS, а загрузку остального CSS отложить, пока страница полностью не загрузится. Критический CSS – это CSS, который необходим для стилизации только той части страницы, которая находится в области видимости экрана при загрузке страницы.
Постарайтесь сократить размер JavaScript файлов настолько, насколько это возможно и помните, что JavaScript файл – это не изображение, браузеру кроме того, что загрузить JS файлы нужно их еще и обработать, что занимает определенное время и ресурсы. Именно от размера JavaScript и от скорости его выполнения зависит такой очень важный показатель как скорость загрузки до взаимодействия. То есть может быть такое, что страница полностью загружена, а пользователь все еще не может с ней взаимодействовать или отклик страницы на действия происходит с большой задержкой. Это значит, что браузер еще не обработал весь JavaScript код.
Также старайтесь откладывать загрузку большого JS кода (файла), который не используется на всех страницах. Например, у Вас может быть компонент весом в 100-200 килобайт, который используется только на одной странице, а загружается на всех страницах. Постарайтесь разделить Ваш JavaScript на несколько кусочков(файлов) и загружать каждый из них только там, где он необходим. Этого легко можно добиться с помощью таких сборщиков как Webpack.
И помните, создание HTML страницы идет сверху вниз, и если браузер по пути отрисовки страницы встретит JS, он «поставит на паузу» отрисовку страницы пока не скачает (если необходимо) и не выполнит его. Поэтому старайтесь, чтобы ваш основной JS файл подключался ниже всего основного HTML, желательно перед закрывающимся тегом body, чтобы не задерживать отрисовку необходимого HTML. Если вы подключаете JS файл не внизу страницы, то используйте атрибут defer при подключении скрипта, что укажет браузеру на то, что это скрипт нужно обрабатывать только после того, как вся страница будет полностью отрисована.
Есть еще один из самых важных пунктов для улучшение скорости загрузки страницы. Это сокращение времени ответа от сервера или времени до получение первого байта (TTFB – Time to first byte). TTFB – это время, которое необходимо, чтобы отправить запрос от клиента (браузера) и получить первый байт данных от веб сервера.
Этот пункт является, пожалуй, самым сложным для оптимизации, потому что он затрагивает довольно много аспектов, многие из которых специфичны для используемых инструментов, подходов к реализации, фреймворков или CMS. И поэтому нет какого-то универсального решения. Но зато есть несколько советов, на что нужно обратить внимание:
Чем быстрее ваш сервер читает или записывает данные и быстрее обрабатывает различные процессы тем быстрее ваш сайт вернет ответ пользователю.
Старайтесь использовать самые последние версии языков программирования, фреймворков, библиотек, CMS, веб сервера или чего-либо еще, что работает на вашем сервере, чтобы пользоваться всеми преимуществами обновлений по производительности, которые могли быть выпущены. Так, PHP 7.0 дает прирост производительности в 180% по сравнению с предыдущей версией 5.6
Посмотрите, может быть вы столкнулись с проблемой n+1, не добавили индексы там, где необходимо или запрашиваете много неиспользуемых данных.
Каждый раз, когда пользователь делает запрос, на сервере делается запрос в базу данных (БД), генерируется HTML из данных и возвращается клиенту. Представьте, если у Вас сайт, на котором вы обновляете контент раз в неделю. Но все равно каждый раз, когда ваш сайт открывается, будет происходить запрос в базу данных, даже если информация не обновлялась. Поэтому, когда первый раз пользователь делает запрос на определенные данные вы можете получить их из БД, показать их пользователю и кэшировать (сохранить локально) эти данные на неделю (например). И после этого, в течение недели, все, кто будут открывать эту страницу уже не будут делать запрос в базу данных каждый раз, а им будет показываться сохраненная копия этих данных. Это будет уменьшать время загрузки страницы и нагрузку на сервер. В некоторых случаях вы можете кэшировать не только данные из БД, а страницы целиком.
Если вы используете SSL, установление защищенного соединения требует больше времени чем незащищенного. Вы можете сделать это самостоятельно или использовать инструменты, которые могут помочь Вам в этом, например Cloudflare. Cloudflare будет выступать как посредник между пользователями и Вашим сайтом, все запросы будут проходить через него. Cloudflare также может справиться с DDoS атаками, которыми может быть подвержен Ваш сайт. Если не обращать на это внимания Ваш сайт может загружаться медленнее после установки SSL.
К ним относятся JS, CSS, файлы, шрифты и изображения. Когда пользователь заходит на Ваш сайт Вы можете дать браузеру выбрать откуда ему брать ресурсы, и это может оказать существенное влияние на время загрузки сайта. Вы можете сохранять статические ресурсы в браузере, когда пользователь первый раз заходит на Ваш сайт. Это и есть кэширование. Вы можете безопасно кэшировать статические ресурсы на долгое время: на 6 месяцев или даже на год. Cloudflare также может кэшировать все статические ресурсы на Вашем сайте. Но, что делать если за это время Вы изменили контент этих файлов, но не изменили название, например добавили пару строчек в свой JS файл? Изменить название файла! Но вы не можете постоянно менять название, но можете добавить какой-нибудь хэш как строку запроса, когда запрашиваете файл. Например, “/js/app.js?v=123456”. Вы будете запрашивать один и тот же файл, но технически для браузера это уже будет другой файл, который еще не был кэширован. Вы можете делать это вручную или, опять же, автоматизировать этот процесс с помощью сборщиков. Это называется контроль версий (versioning) или очистка кэша (cache busting).
Попробуйте применить эти несложные советы для оптимизации скорости загрузки Вашего сайта, и я уверен время его загрузки существенно сократится.
Работа с файлами в JavaScript
Доброго времени суток, друзья!
Мнение о том, что JavaScript не умеет взаимодействовать с файловой системой, является не совсем верным. Скорее, речь идет о том, что это взаимодействие существенно ограничено по сравнению с серверными языками программирования, такими как Node.js или PHP. Тем не менее, JavaScript умеет как получать (принимать), так и создавать некоторые типы файлов и успешно обрабатывать их нативными средствами.
В этой статье мы создадим три небольших проекта:
Получаем и обрабатываем файлы
Для начала создадим директорию, в которой будут храниться наши проекты. Назовем ее «Work-With-Files-in-JavaScript» или как Вам будет угодно.
В этой директории создадим папку для первого проекта. Назовем ее «File-Reader».
Создаем в ней файл «index.html» следующего содержания:
Здесь мы имеем контейнер-файлоприемник и инпут с типом «file» (для получения файла; мы будем работать с одиночными файлами; для получения нескольких файлов инпуту следует добавить атрибут «multiple»), который будет спрятан под контейнером.
Стили можно подключить отдельным файлом или в теге «style» внутри head:
Можете сделать дизайн по своему вкусу.
Не забываем подключить скрипт либо в head с атрибутом «defer» (нам нужно дождаться отрисовки (рендеринга) DOM; можно, конечно, сделать это в скрипте через обработку события «load» или «DOMContentLoaded» объекта «window», но defer намного короче), либо перед закрывающим тегом «body» (тогда не нужен ни атрибут, ни обработчик). Лично я предпочитаю первый вариант.
Откроем index.html в браузере:
Прежде чем переходить к написанию скрипта, следует подготовить файлы для приложения: нам потребуется изображение, аудио, видео, текст в формате txt, pdf и любом другом, например, doc. Можете использовать мою коллекцию или собрать свою.
Нам часто придется обращаться к объектам «document» и «document.body», а также несколько раз выводить результаты в консоль, поэтому предлагаю обернуть наш код в такое IIFE (это не обязательно):
Первым делом объявляем переменные для файлоприемника, инпута и файла (последний не инициализируем, поскольку его значение зависит от способа передачи — через клик по инпуту или бросание (drop) в файлоприемник):
Отключаем обработку событий «dragover» и «drop» браузером:
Для того, чтобы понять, зачем мы это сделали, попробуйте перенести изображение или другой файл в браузер и посмотрите, что произойдет. А происходит автоматическая обработка файлов, т.е. то, что мы собираемся реализовать самостоятельно в познавательных целях.
Обрабатываем бросание файла в файлоприемник:
Мы только что реализовали простейший механизм «dran’n’drop».
Обрабатываем клик по файлоприемнику (делегируем клик инпуту):
Приступаем к обработке файла:
Удаляем файлоприемник и инпут:
Способ обработки файла зависит от его типа:
Мы не будем работать с html, css и js-файлами, поэтому запрещаем их обработку:
Мы также не будем работать с MS-файлами (имеющими MIME-тип «application/msword», «application/vnd.ms-excel» и т.д.), поскольку их невозможно обработать нативными средствами. Все способы обработки таких файлов, предлагаемые на StackOverflow и других ресурсах, сводятся либо к конвертации в другие форматы с помощью различных библиотек, либо к использованию viewer’ов от Google и Microsoft, которые не хотят работать с файловой системой и localhost. Вместе с тем, тип pdf-файлов также начинается с «application», поэтому такие файлы мы будем обрабатывать отдельно:
Для остальных файлов получаем их «групповой» тип:
Посредством switch..case определяем конкретную функцию обработки файла:
Функция обработки изображения:
Функция обработки аудио:
Функция обработки видео:
Функция обработки текста:
Last, but not least, функция обработки pdf-файлов: