HTML5 предоставляет нам целый ряд новых возможностей, таких как рисование на холсте , реализация мультимедиа с помощью API аудио и видео и так далее. Одним из этих инструментов, который все еще является относительно новым, является API файловой системы . Это дает нам доступ к изолированному разделу локальной файловой системы пользователя, тем самым еще больше заполняя разрыв между настольными и веб-приложениями! В сегодняшнем уроке мы рассмотрим основы этого нового и интересного API, исследуя наиболее распространенные задачи файловой системы. Давайте начнем!
Вступление
Нам больше не нужно загружать и устанавливать определенное программное обеспечение, чтобы использовать его. Просто веб-браузер и подключение к Интернету дают нам возможность использовать любое веб-приложение в любое время, в любом месте и на любой платформе.
Короче говоря, веб-приложения это круто; но по сравнению с настольными приложениями у них все еще есть один существенный недостаток: у них нет способа взаимодействовать и организовывать данные в структурированную иерархию папок — настоящую файловую систему. К счастью, с помощью нового API файловой системы это можно изменить. Этот API предоставляет веб-приложениям контролируемый доступ к частной локальной файловой системе «песочницы», в которой они могут записывать и читать файлы, создавать и перечислять каталоги и т. Д. Хотя на момент написания этой статьи только браузер Google Chrome поддерживал «полную» реализацию API файловой системы, он все еще заслуживает изучения как мощная и удобная форма локального хранилища.
Файловая система API поставляется в двух разных версиях. Асинхронный API, который полезен для обычных приложений, и синхронный API, зарезервированный для использования с веб-работниками. В целях данного руководства мы будем изучать только асинхронную версию API.
Шаг 1 — Начало работы
Ваш первый шаг — получить доступ к файловой системе HTML5, запросив системный объект window.requestFileSystem()
глобального метода window.requestFileSystem()
:
window.requestFileSystem (тип, размер, successCallback, opt_errorCallback)
Веб-приложение не может «вырваться» за пределы локального корневого каталога.
В качестве первых двух параметров вы указываете время жизни и размер желаемой файловой системы. PERSISTENT файловая система подходит для веб-приложений, которые хотят постоянно хранить пользовательские данные. Браузер не удалит его, кроме как по явному запросу пользователя. ВРЕМЕННАЯ файловая система подходит для веб-приложений, которые хотят кэшировать данные, но все еще могут работать, если веб-браузер удаляет файловую систему. Размер файловой системы указывается в байтах и должен быть разумным верхним пределом объема данных, которые необходимо хранить.
Третий параметр — это функция обратного вызова, которая запускается, когда пользовательский агент успешно предоставляет файловую систему. Его аргумент является объектом FileSystem
. И, наконец, мы можем добавить дополнительную функцию обратного вызова, которая вызывается при возникновении ошибки или при отклонении запроса на файловую систему. Его аргумент является объектом FileError
. Хотя этот параметр является необязательным, всегда полезно отлавливать ошибки для пользователей, поскольку существует ряд мест, где что-то может пойти не так.
Файловая система, полученная с помощью этих функций, зависит от происхождения содержащего документа. Все документы или веб-приложения из одного источника (хост, порт и протокол) совместно используют файловую систему. Два документа или приложения из разных источников имеют совершенно разные и непересекающиеся файловые системы. Файловая система ограничена одним приложением и не может получить доступ к данным другого приложения. Он также изолирован от остальных файлов на жестком диске пользователя, и это хорошо: веб-приложение не может «вырваться» за пределы локального корневого каталога или иным образом получить доступ к произвольным файлам.
Давайте рассмотрим пример:
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem; window.requestFileSystem (window.TEMPORARY, 5 * 1024 * 1024, initFS, errorHandler); функция initFS (fs) { alert («Добро пожаловать в файловую систему! Это время показа :)»); // Просто чтобы проверить, все ли в порядке 🙂 // разместите функции, которые вы изучите ниже } function errorHandler () { console.log («Произошла ошибка»); }
Это создает временную файловую систему с 5 МБ памяти. Затем он предоставляет функцию обратного вызова, которую мы будем использовать для работы нашей файловой системы. И, конечно же, добавлен обработчик ошибок — на случай, если что-то пойдет не так. Здесь errorHandler()
является слишком общей. Поэтому, если вы хотите, вы можете создать слегка оптимизированную версию, которая дает читателю более понятное сообщение об ошибке:
function errorHandler (err) { var msg = 'Произошла ошибка:'; switch (код ошибки) { case FileError.NOT_FOUND_ERR: msg + = 'Файл или каталог не найден'; перемена; case FileError.NOT_READABLE_ERR: msg + = 'Файл или каталог не читаются'; перемена; case FileError.PATH_EXISTS_ERR: msg + = 'Файл или каталог уже существует'; перемена; case FileError.TYPE_MISMATCH_ERR: msg + = 'Неверный тип файла'; перемена; дефолт: msg + = 'Неизвестная ошибка'; перемена; }; console.log (MSG); };
Получаемый вами объект файловой системы имеет name
(уникальное имя для файловой системы, назначаемое браузером) и root
свойство, которое относится к корневому каталогу файловой системы. Это объект DirectoryEntry
, и он может иметь вложенные каталоги, которые сами представлены объектами DirectoryEntry
. Каждый каталог в файловой системе может содержать файлы, представленные объектами FileEntry
. Объект DirectoryEntry
определяет методы для получения объектов DirectoryEntry
и FileEntry
по пути (они могут создавать новые каталоги или файлы, если вы укажете несуществующее имя). DirectoryEntry
также определяет фабричный метод createReader()
который возвращает объект DirectoryReader
для перечисления содержимого каталога. Класс FileEntry
определяет метод для получения объекта File
(Blob), который представляет содержимое файла. Затем вы можете использовать объект FileReader
для чтения файла. FileEntry
определяет другой метод для возврата объекта FileWriter
, который можно использовать для записи содержимого в файл.
Фуф … звучит сложно? Не беспокойся Все станет яснее по мере продвижения по примерам ниже.
Шаг 2 — Работа с каталогами
Очевидно, что первое, что вам нужно создать в файловой системе, это несколько блоков или каталогов. Хотя корневой каталог уже существует, вы не хотите помещать туда все свои файлы. Каталоги создаются объектом DirectoryEntry
. В следующем примере мы создаем каталог с именем Documents
в корневом каталоге:
fs.root.getDirectory ('Documents', {create: true}, function (dirEntry) { alert ('Вы только что создали каталог' + dirEntry.name + '.'); }, errorHandler);
Метод getDirectory()
используется как для чтения, так и для создания каталогов. В качестве первого параметра вы можете указать имя или путь в качестве каталога для поиска или создания. Мы устанавливаем для второго аргумента значение true
, потому что мы пытаемся создать каталог, а не читать существующий. И в конце добавляем сообщение об ошибке.
Все идет нормально. У нас есть каталог; давайте теперь добавим подкаталог. Функция точно такая же с одним отличием: мы меняем первый аргумент с «Документы» на «Документы / Музыка». Достаточно просто; но что, если вы хотите создать подпапку Sky
с двумя родительскими папками Images
и Nature
внутри папки Documents
? Если вы введете « Documents/Images/Nature/Sky
» в качестве аргумента пути, вы получите сообщение об ошибке, поскольку вы не можете создать каталог, если его непосредственный родитель не существует. Решением для этого является создание каждой папки одна за другой: Images
внутри Documents
, Nature
внутри Images
, а затем Sky
внутри Nature
. Но это очень медленный и неудобный процесс. Есть лучшее решение: создать функцию, которая будет автоматически создавать все необходимые папки.
функция createDir (rootDir, папки) { rootDir.getDirectory (папки [0], {create: true}, функция (dirEntry) { if (folder.length) { createDir (dirEntry, folder.slice (1)); } }, errorHandler); }; createDir (fs.root, 'Documents / Images / Nature / Sky /'. split ('/'));
С помощью этой маленькой хитрости все, что нам нужно сделать, это предоставить полный путь, представляющий папки, которые мы хотим создать. Теперь каталог Sky
успешно создан, и вы можете создавать в нем другие файлы или каталоги.
Теперь пришло время проверить, что у нас есть в нашей файловой системе. Мы создадим объект DirectoryReader
и используем метод readEntries()
для чтения содержимого каталога.
fs.root.getDirectory ('Documents', {}, function (dirEntry) {<br> var dirReader = dirEntry.createReader (); dirReader.readEntries (function (records) {<br> for (var i = 0; i <records.length; i ++) { var entry = records [i]; if (entry.isDirectory) { console.log ('Directory:' + entry.fullPath); } else if (entry.isFile) { console.log ('File:' + entry.fullPath); } } }, errorHandler); }, errorHandler);
В приведенном выше коде свойства isDirectory
и isFile
используются для получения различного вывода для каталогов и файлов соответственно. Кроме того, мы используем свойство fullPath
, чтобы получить полный путь к записи, а не только ее имя.
Есть два способа удалить DirectoryEntry
из файловой системы: remove()
и removeRecursively()
. Первый удаляет данный каталог, только если он пуст. В противном случае вы получите ошибку.
fs.root.getDirectory ('Документы / Музыка', {}, функция (dirEntry) { dirEntry.remove (функция () { console.log ('Каталог успешно удален.'); }, errorHandler); }, errorHandler);
Если в папке « Music
» есть файлы, вам нужно использовать второй метод, который рекурсивно удаляет каталог и все его содержимое.
fs.root.getDirectory ('Документы / Музыка', {}, функция (dirEntry) { dirEntry.removeRecursively (функция () { console.log ('Каталог успешно удален.'); }, errorHandler); }, errorHandler);
Шаг 3 — Работа с файлами
Теперь, когда мы знаем, как создавать каталоги, пришло время заполнить их файлами!
В следующем примере создается пустой test.txt
в корневом каталоге:
fs.root.getFile ('test.txt', {create: true, exclusive: true}, функция (fileEntry) { alert ('Файл' + fileEntry.name + 'был успешно создан.'); }, errorHandler);
Первый аргумент getFile()
может быть абсолютным или относительным путем, но он должен быть действительным. Например, ошибочно пытаться создать файл, когда его непосредственный родитель не существует. Второй аргумент — это литерал объекта, описывающий поведение функции, если файл не существует. В этом примере create: true
создает файл, если он не существует, и выдает ошибку, если он существует ( exclusive: true
). В противном случае, если create: false
, файл просто выбирается и возвращается.
Наличие пустого файла не очень полезно; так что давайте добавим контент внутри. Мы можем использовать объект FileWriter
для этого.
fs.root.getFile ('test.txt', {create: false}, function (fileEntry) { fileEntry.createWriter (function (fileWriter) { window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder; var bb = new BlobBuilder (); bb.append («API файловой системы потрясающий!»); fileWriter.write (bb.getBlob ( 'текст / обычный')); }, errorHandler); }, errorHandler);
Выше мы извлекаем файл test.txt
и создаем для него объект FileWriter
. Затем мы добавляем к нему содержимое, создавая новый объект BlobBuilder
и используя метод write()
FileWriter
.
Вызов getFile()
возвращает только FileEntry
. Он не возвращает содержимое файла. Итак, если мы хотим прочитать содержимое файла, нам нужно использовать объект File
объект FileReader
.
fs.root.getFile ('test.txt', {}, функция (fileEntry) { fileEntry.file (function (file) { var reader = new FileReader (); reader.onloadend = function (e) { оповещения (this.result); }; reader.readAsText (файл); }, errorHandler); }, errorHandler);
Мы записали некоторый контент в наш файл, но что если вы захотите добавить больше на более поздний срок? Чтобы добавить данные в существующий файл, FileWriter
используется еще раз. Мы можем переместить писателя в конец файла, используя метод seek()
. seek
принимает смещение байта в качестве аргумента и устанавливает позицию записывающего файла в это смещение.
fs.root.getFile ('test.txt', {create: false}, function (fileEntry) { fileEntry.createWriter (function (fileWriter) { fileWriter.seek (fileWriter.length); window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder; var bb = new BlobBuilder (); bb.append («Да, это так!»); fileWriter.write (bb.getBlob ( 'текст / обычный')); }, errorHandler); }, errorHandler);
Чтобы удалить файл из файловой системы, просто вызовите entry.remove()
. Первым аргументом этого метода является функция обратного вызова с нулевым параметром, которая вызывается при успешном удалении файла. Вторым является необязательный обратный вызов ошибок, если возникают какие-либо ошибки.
fs.root.getFile ('test.txt', {create: false}, function (fileEntry) { fileEntry.remove (function () { console.log ('Файл успешно удален.'); }, errorHandler); }, errorHandler);
Шаг 4 — Управление файлами и каталогами
FileEntry
и DirectoryEntry
одни и те же методы API для копирования, перемещения и переименования записей. Для этих операций вы можете использовать два метода: copyTo()
и moveTo()
. Они оба принимают одинаковые параметры:
copyTo (parentDirEntry, opt_newName, opt_successCallback, opt_errorCallback); moveTo (parentDirEntry, opt_newName, opt_successCallback, opt_errorCallback);
Первый параметр — это родительская папка, в которую нужно переместить / скопировать запись. Второе — необязательное новое имя, чтобы дать перемещенную / скопированную запись, которая фактически требуется, когда вы копируете запись в той же папке; в противном случае вы получите ошибку. Третий и четвертый параметры были объяснены ранее.
Давайте рассмотрим несколько простых примеров. В следующем test.txt
мы копируем файл test.txt
из root
каталога в каталог Documents
.
функция copy (currDir, srcEntry, destDir) { currDir.getFile (srcEntry, {}, функция (fileEntry) { currDir.getDirectory (destDir, {}, function (dirEntry) { fileEntry.copyTo (dirEntry); }, errorHandler); }, errorHandler); } копия (fs.root, 'test.txt', 'Documents /');
Следующий пример перемещает test.txt
в Documents
вместо его копирования:
функция move (currDir, srcEntry, dirName) { currDir.getFile (srcEntry, {}, функция (fileEntry) { currDir.getDirectory (dirName, {}, функция (dirEntry) { fileEntry.moveTo (dirEntry); }, errorHandler); }, errorHandler); } move (fs.root, 'test.txt', 'Documents /');
Следующий пример переименовывает test.txt
в text.txt
:
переименование функции (currDir, srcEntry, newName) { currDir.getFile (srcEntry, {}, функция (fileEntry) { fileEntry.moveTo (currDir, newName); }, errorHandler); } переименовать (fs.root, 'test.txt', 'text.txt');
Учить больше
В этом вводном уроке мы только коснулись поверхности различных интерфейсов файловой системы. Если вы хотите узнать больше и углубиться в API файловой системы, вам следует обратиться к спецификациям спецификаций W3C:
Теперь, когда у вас есть общее представление о том, что такое API файловой системы и как его можно использовать, вам будет значительно легче понять документацию по API, что на первый взгляд может показаться немного запутанным.
Вывод
Файловая система API — это мощная и простая в использовании технология, которая предоставляет веб-разработчикам целый ряд новых возможностей при создании веб-приложений. По общему признанию, это все еще довольно ново и не широко поддержано всеми основными браузерами, но это конечно изменится в будущем. Вы могли бы также получить преимущество!