файловая система и javascript

Существует множество задач, связанных с созданием, парсингом, сохранением и удалением файлов. Большинство из них выполняется на стороне сервера. Прежде всего потому, что Java, C# или любой другой язык, который вы используете на сервере, может легко справиться со всеми этими задачами.

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

Вторая причина, по которой вы все еще выполняете вышеуказанные действия на сервере, заключается в том, что вы просто еще не прочитали данную статью и не знаете всех возможностей современных браузеров. Не волнуйтесь! Мы откроем вам все их секреты и тайны.

Данную тему можно разделить на три части: прошлое, настоящее и будущее.

Прошлое. ActiveX

В прошлом существовало только два браузера: Netscape и IE. Однажды Microsoft решил порадовать пользователей IE и добавил невероятную для того времени функциональность, которая позволяла коду, написанному на языке JavaScript, обмениваться информацией с операционной системой Windows. Данный способ получил название ActiveX.

Описанная технология может применяться даже сегодня в последней версии браузера IE (по умолчанию, ActiveX отключен). Не будем долго останавливаться на этой теме, поскольку ActiveX актуален только для IE, и пользователю необходимо проделать много необычных действий со своим браузером для того, чтобы запустить сценарий, который содержит ActiveXObjects.

В разделе, посвященном настоящему времени, мы поговорим об API для работы с файлами и Drag-and-Drop, а также о некоторых интересных примерах.

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

Настоящее. Управление добавленными файлами

Существует два способа передать файл в современный браузер (IE10+):

1) Input тег с типом “file”;
2) Перетянуть файлы на какой-либо элемент DOM-модели.

Старые добрые времена с элементом Input

Первый вариант может использоваться даже в старых браузерах. Единственное отличие в том, что мы не можем получить содержимое файлов в IE9 и более ранних версий. Это означает, что для выполнения некоторых действий нам все еще необходимо использовать сервер. Чтобы получить файлы, выбранные пользователем, нам нужно добавить обработчик события “change” и проверять свойство “files” данного элемента при вызове функции-обработчика.

 

Как видите, здесь мы использовали метод “readAsText” объекта “FileReader”, чтобы получить содержимое файла. У него также есть методы, которые читают файл(файлы) и возвращают результаты в различных форматах.

Вы можете использовать метод „readAsArrayBuffer“, который читает файл, и возвращает файлы в виде массива данных в двоичном формате; „readAsDataURL“, который возвращает URL в виде “data:image/png,base64:”. Вы можете поместить этот URL в атрибуте src тега IMG, например (смотрите демо #2).

Будьте внимательны, используя метод “readAsBinaryString”, поскольку он не поддерживается браузером IE версий 10-11.

Возможно, вы также хотите контролировать процесс чтения файла, отобразить индикаторы прогресса или ожидания, или прервать его, если файлы слишком объемные. Специально для вас у FileReader есть такие события как onprogress, onabort, onloadstart, onloadend, onerror, а также метод abort, который останавливает процесс чтения и выдает ошибку, которая может быть обработана.

В данном примере мы немного исправили функциональность для IE9, у которого нет возможности чтения файлов. Из информации о выбранном файле (множественный выбор не поддерживается) мы можем получить только его имя, которое хранится в свойстве “value” элемента input.

Выберите картинку:

Перетащи меня в браузер!

Это был старый способ добавления файлов в браузер. Он мог использоваться даже в IE6 (addEventListener поддерживается в IE9+), с некоторыми изменениями. В современных браузерах (IE10+) существует другой способ добавления файлов. Все, что нужно сделать, это повесить обработчик события «drop” на любой элемент веб-страницы. Это может быть как специальный элемент с текстом внутри, так и просто body. После этого пользователь может перетащить какой-либо файл в элемент, на котором висит обработчик события “drop”. Выбранный файл можно найти в свойстве “dataTransfer” объекта события. Давайте посмотрим на демо:

Перетащите свой файл сюда

Несколько важных замечаний по этому демо:

1) Прежде всего, вам не стоит проверять, поддерживает ли ваш браузер событие “ondrop”, поскольку используется IE9. Он поддерживает данное событие, но не имеет свойства dataTransfer.

2) Если вы хотите поймать событие “ondrop”, вам необходимо добавить обработчик события “ondragover” и вызвать функцию preventDefault. По умолчанию, браузер попытается загрузить этот файл или отобразить его на странице (если это рисунок или PDF файл), и вам нужно его остановить.

Поддержка старых версий браузеров: в таких браузерах, как IE9, вы можете поместить элемент input из предыдущих примеров на текущем элементе, растянуть его, установить для него прозрачность равную 100%, и изменить текст на следующий: “Кликните и выберите файл”. Когда пользователь кликнет на этот элемент, он увидит обычный диалог для выбора файлов. Подобное решение подходит также для планшетов и телефонов, у которых нет возможности разделить экраны и перетягивать элементы между ними.

Перетащи меня из браузера!

Предыдущий пример мог вызвать следующий вопрос о функциональности drag-and-drop: “Можем ли мы перетащить файлы из браузера на компьютер?” Ответ будет “Да”, но работает это только в браузере Chrome. Тем не менее, это отличная функция для вашей веб страницы, и мы должны ее испробовать. В следующем примере вам нужно перетянуть картинку в формате PDF на свой компьютер, и она будет сохранена как PDF файл.

Перетащите эту картинку из браузера на ваш рабочий стол

Код в данном примере достаточно прост. Все, что вам нужно сделать — это добавить обработчик события “dragstart” и задать путь к данным, которые необходимо переместить. Это может быть ссылка на какой-либо документ на сервере или двоичные данные. Если вы хотите получить файл с сервера, нужно передать специальную строку, состоящую из типа содержимого, имени файла, который будет сохранен, и полного пути к нему. Например: “application/pdf:newDoc.pdf:https://mywebsite.com”.

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

“Все течет, все изменяется … и … дважды в одну и ту же реку войти невозможно”. (Гераклит)

Теперь мы можем получить файлы и их содержимое в формате JavaScript. Но что мы можем с ними делать? Как мы можем их изменить? Вот в чем вопрос.

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

Примером также может быть парсинг файла в формате excel. Вам не нужно посылать его на сервер, конвертировать в JSON и отправлять обратно. Это можно сделать на клиентской стороне, не тратя времени на запрос и ожидание ответа сервера. Уже есть библиотеки для парсинга xls и xlsx файлов, которые вы можете найти здесь: XLS и XLSX.

Еще один способ использования файлов и их содержимого в JavaScript — это работа с двоичными данными. Это могут быть рисунки, видео или какие-либо другие файлы. Если мы хотим обрабатывать двоичные данные, нам нужно использовать новые свойства JavaScript, такие как объект Blob и типизированные массивы. Мы не будем углубляться в эту тему, потому что она слишком большая и заслуживает отдельной статьи.

Объект Blob — это объект, который выглядит, как обычный файл на JavaScript, и содержит необработанные данные. Конструктор такого объекта принимает два параметра — данные и объект с его опциями. В состав данных могут входить ArrayBuffer, типизированный массив, объект Blob или строки. Это все те данные, которые будут храниться в новом объекте Blob. Опции включают в себя тип данных.

У каждого объекта Blob есть тип и свойства, которые задают его размеры, а также метод “slice”. Все данные, которые содержатся в данном объекте Blob, можно разделить с помощью метода “slice”. Этот метод возвращает новый объект Blob, который содержит в себе отделенную часть данных.

Типизированные массивы и буферы используются для хранения двоичных данных фиксированной длины и определенного типа (Int8Array или Int32Array).

Все это позволяет нам делать занимательные вещи на клиентской стороне, например, сжимать файлы. Эта библиотека позволяет применить алгоритм Deflate ко всем выбранным файлам, и собрать их в один zip файл. Кроме того, она использует Web Workers, чтобы описанный процесс протекал в фоновом режиме.

Еще один пример обработки файла, содержащего двоичные данные, это PDF.js. Это проект, который находится в открытом доступе и используется для отображения на веб-странице документов в формате PDF. Все, что вам нужно сделать, это передать ему массив двоичных данных, и он отрисует содержимое PDF-документа на странице.

Будущее. Создайте свою файловую систему с блэкджеком и… всеми причитающимися атрибутами

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

Во время написания этой статьи данная функциональность поддерживалась только браузерами Chrome и Opera. Опять-таки, это не такая уж важная проблема, поскольку данная возможность может использоваться как дополнительный инструмент для кэширования файлов в вашем браузере. Давайте более детально рассмотрим ее API.
Прежде всего, нам нужно запросить объект файловой системы с помощью метода webkitRequestFileSystem. Он принимает 4 параметра: тип, размер, функция, которая будет вызвана в случае успешной обработки запроса, и функция, которая будет вызвана при возникновении ошибки.

В браузере имеются два типа файловой системы: временная и постоянная. Первая может быть создана без запроса разрешения пользователя. Вы просто ее создаете и затем используете. Однако, если ваш браузер столкнется с какой-либо проблемой, как, например, недостаток памяти, он удалит все хранящиеся в нем данные. Поэтому данный тип хранения информации может быть использован только для кэширования данных, которые можно восстановить.

Еще один тип файловой системы — постоянный. Браузер никогда не удалит файлы, хранящиеся в ней. Тем не менее, если вы запросите данный тип файловой системы без разрешения пользователя, она не будет работать.

Для того, чтобы запросить разрешение пользователя на использование постоянного хранилища, нам нужно вызвать метод navigator.webkitPersistentStorage.requestQuota.

Константы обоих типов файловых систем хранятся в объекте window: window.TEMPORARY или window.PERSISTENT. Второй параметр — это нужный нам размер хранилища в байтах.

Теперь мы можем создать наше первое хранилище вместимостью 1Гб:
Временная файловая система:

Постоянная файловая система:

Как видите, мы используем много обработчиков ошибок, которые должны отлавливать ошибки, связанные с созданием хранилищ. Объект FileError содержит полный список типов ошибок:

объект FileError

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

Данный объект имеет интерфейс следующего вида:

объект файловой системы

Как видите, для создания, перемещения, удаления и получения файлов и директорий используются специальные методы. Полученный нами объект “fs” ссылается на корневой каталог файловой системы браузера.

Данный каталог и все другие каталоги, которые мы будем создавать, представлены интерфейсом DirectoryEntry. Все файлы данной файловой системы представлены интерфейсом FileEntry. Мы будем использовать методы getFile и getDirectory для того, чтобы создать файлы и папки и получить их.

Первый параметр данных методов — это файл или путь к нему. Этот путь может быть как абсолютным, так и относительным. Второй параметр — это объект с действиями, которые выполняются в случае, если в указанной директории файл отсутствует. Данный параметр может иметь свойства “create” и “exclusive”.
Если свойству “create” задано значение “true”, будет создан новый файл или директория, а старый файл будет переписан. Если же мы для данного свойства зададим значение “false”, методы вернут объект FileEntry или выдадут ошибку, если файла не существует. Если задано свойство “exclusive” со значением “true”, файлы не будут переписаны.

Для того, чтобы различить файлы и папки, у каждого из двух интерфейсов есть флаги: isDirectory и isFile. Кроме того, существует специальный класс FileWriter, который служит для добавления содержимого в файл. Его необходимо запрашивать у браузера для каждого файла, который нужно изменить. Давайте добавим несколько файлов:

Все прошло успешно и, вроде бы, без ошибок. Давайте проверим наши файл и папку. Необходимо запустить некоторые экспериментальные функции в DevTool браузера Chrome, чтобы увидеть содержимое файловой системы браузера. Поэтому, вам нужно перейти на страницу “chrome://flags/#enable-devtools-experiments”. После перезагрузки Chrome, в настройках DevTool вы увидите вкладку “Эксперименты”. Здесь вам нужно включить опцию “File System inspection” и перезапустить DevTool.

dev tool

После этого вы увидите опцию “FileSystem” на вкладке “Resources” раздела DevTool. Здесь отображаются все созданные файлы и хранилища.

файлы и хранилища

Данные файлы мы также можем получить по специальному внутреннему URL браузера, который может быть установлен в качестве атрибута “src” тега IMG. Он начинается с слова “filesystem:” и выглядит вот так: “filesystem:{URL of site}/persistent/files/newFile.txt”.
Чтобы получить URL, мы использовали метод “toURL”.
Теперь у нас есть вся необходимая информация для того, чтобы создать веб-приложение для кэширования аудио и видео файлов, которое позволяет слушать и смотреть их без подключения к интернету. Прежде всего, загрузим двоичный файл. Затем, мы сохраним его в постоянном хранилище и запустим.

Благодарим за внимание и ждем ваших комментариев!