Logo Море(!) аналитической информации!
IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware
Конференция «Технологии управления данными 2018»
СУБД, платформы, инструменты, реальные проекты.
29 ноября 2018 г.
2008 г.

Хранение данных на клиенте. DOM Storage и его аналоги

Илья Кантор, javascript.ru
Новые версии статьи публикуются на сайте автора.

По мере того как web-странички превращаются в AJAX-приложения, им требуются все новые возможности.

Сложные выборки элементов DOM обеспечиваются некоторыми браузерами и почти всеми распространенными Javascript-фреймворками.

Кросс-доменные HTTP-запросы находят поддержку в стандартах и реализуются в новейших браузерах, включая Internet Explorer 8.

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

В частности, Internet Explorer 5+, Firefox 2+, Safari 2+ не требуют для этого дополнительных плагинов и Flash.

Зачем нужны дополнительные средства хранения?

Почти во всех браузерах есть поддержка cookies.

На протяжении долгого времени cookies были единственным кросс-браузерным способом сохранить данные, которые будут доступны после перезагрузки страницы.

Однако у cookie есть две важных особенности:

  1. Не более 2 килобайт данных
  2. Данные идут на сервер при каждом HTTP-запросе

Средства хранения на клиенте предусматривают сотни килобайт и мегабайты данных, и не отсылают их на сервер при каждом HTTP-запросе.

А cookie можно продолжать использовать, например, для хранения сессии.

Firefox (Gecko). Стандарт HTML 5.

Firefox реализует стандарт хранения "Client-side session and persistent storage of name/value pairs", предложенный в спецификации HTML 5.

Для постоянного хранения данных в нем используется объект window.globalStorage[домен], операции над которым можно производить точно так же, как над обычным javascript-объектом. При уходе с сайта и даже закрытии браузера globalStorage не меняется, так что все его свойства можно прочитать обратно.

Например:

storage = globalStorage[document.domain]
 
// записать значение
storage['userName'] = 'Vasya'
// прочитать значение
alert(storage['userName'])
// удалить значение

delete storage['userName']
 
// получить все значения
for(var name in storage) {

    alert(name + ':' + storage[name])
}

При чтении/записи на элементе body инициируется всплывающее событие storage.

Поймать его можно, например, таким обработчиком:

window.addEventListener('storage', function(event) { ... })

Стандарт HTML 5 все еще в процессе развития. В старой редакции прочитанные значения имели тип StorageItem.

Версия Firefox 2.0.0.13 возвращает при чтении объект именно этого типа.

Из текущей редакции StorageItem убран. В ней возвращаемые хранилищем значения имеют более простой тип DOMString.

..А пока эти изменения не учтены разработчиками, рекомендуется преобразовать значения к String явным образом.

Например:

var test = "12345"
storage.test = test // сохранить -> String

test = storage.test // прочитать <- StorageItem
 
alert(test.length) // undefined, это же не строка
alert(test.constructor) // StorageItem

test = String(test) // сделали строку. Теперь все ок.
Ограничения

Ограничения на данные: ключи и значения - только строки.

Размер: 5MB на домен.

Ограничения безопасности - точно такие же, как на cookie.
Данные, в globalStorage['site.ru'] можно сохранить только на самом site.ru, а прочитать - на blog.site.ru, но не на otherhost.ru.

Дополнительные материалы

Internet Explorer. userData.

Internet Explorer 8 реализует DOM Storage, в то время как версии начиная от 5й поддерживают собственный интерфейс: userData behavior.

Он работает посредством выделенного DOM-элемента, которому назначается behavior userData. В этот элемент загружается нужное пространство имен, и данные становятся доступны через атрибуты.

<span id="storageElement"></span>
<script>    
    storage = document.getElementById('storageElement')
    if (!storage.addBehavior) {
        alert("userData not available.")
    } else {
        // поставить userData behavior
        storage.addBehavior("#default#userData")
        // загрузить пространство имен
        storage.load("namespace")    
    }

</script>
После инициализации можно работать с данными. Для записи изменений используется метод save.
function put(key, value) { // записать значение

    storage.setAttribute(key, value)
    storage.save("namespace")
}
 
function get(key) { // получить значение

    return storage.getAttribute(key)
}
 
function remove(key) { // удалить значение

    storage.removeAttribute(key)
    storage.save("namespace")
}

Как это часто бывает с Internet Explorer, некоторые операции делаются неочевидным :) образом.

Так, например, получить все сохраненные данные из storage.attributes нельзя. Там хранятся только атрибуты самого HTML-элемента.

Данные же хранятся в свойстве storage.XMLDocument.documentElement.attributes.

Например, следующий код создает список вида ключ:значение.

var list = []
var attrs = storage.XMLDocument.documentElement.attributes
 
for(var i=0; i<attrs.length; i++) {
    list.push(attrs[i].name+':'+attrs[i].value);
}
Устаревание, атрибут expires

В отличие от DOM Storage, можно задать атрибут expires. Он устанавливается на уровне всего элемента и действует на все хранящиеся данные. Очистка данных происходит при вызове load.

var time = new Date(); // Start Time

time.setMinutes(time.getMinutes() + 1)
storage.expires = time.toUTCString();
Устаревание, атрибут expires

В отличие от DOM Storage, можно задать атрибут expires. Он устанавливается на уровне всего элемента и действует на все хранящиеся данные. Очистка данных происходит при вызове load.

var time = new Date(); // Start Time

time.setMinutes(time.getMinutes() + 1)
storage.expires = time.toUTCString();
Ограничения

Ключи и значения - только строки.

Способ работает при всех уровнях безопасности, кроме "Высокого".
При этом для сайтов в зоне Internet объем ограничен 128K на страницу и 1024K на домен, для локальных и интранет - лимит увеличен.

Ограничения безопасности - та же директория, тот же домен и протокол.

Дополнительные материалы

Safari(WebKit). Database storage.

Дальше всех в поддержке стандарта хранения пошли разработчики WebKit.
В Safari реализовано локальное хранение в базе данных SQLite.

Набор операций включает в себя CREATE TABLE, INSERT, SELECT, REPLACE, индексы и многое другое, с рядом ограничений безопасности (например, нет LOAD DATA INFILE).

В отличие от DOM Storage и userData, этот интерфейс асинхронный. Все функции запросов к базе данных принимают в качестве аргументов две функции: callback - для обработки результатов и errback - для обработки ошибок.

Когда запрос завершается, вызывается один из этих обработчиков.

Продемонстрируем это на тестовой базе.

db = openDatabase("Test", "1.0", "Webkit Storage Example")

 
db.transaction(function(tx) {
  tx.executeSql(
      "CREATE TABLE IF NOT EXISTS test (key TEXT, value TEXT, unique(key))", 
      [], 
      function(tx, result) { alert("Success!") },
      function(tx, error) { alert("Failure: "+error.message }

  )
})

Сложновато с первого взгляда?

db.transaction создает транзакцию и передает ее функции-аргументу.

Код внутри function(tx) выполняется в одной транзакции.

Вызов tx.executeSql принимает аргументы:

  1. Запрос
  2. Аргументы подстановки
  3. Обработчик результата
  4. Обработчик ошибки

Следующий пример демонстрирует обработку запроса.

db.transaction(function(tx) {
  tx.executeSql("SELECT value FROM test WHERE key=?", [key],
    function(tx,result) {

      alert("Количество результатов: "+result.rows.length)
      alert("Поле value первого результата: "+ result.rows.item(0).value)

   },
   function(tx, error) { alert("Error!") }
  )
})
Ограничения

Стандарт SQL-хранения также включает в себя поддержку версий схемы, указание размера базы данных в openDatabase и многое другое. Может существовать только одна версия схемы одновременно.

База существует только в рамках домена(полного домена, origin), на котором была создана. Поддомен не имеет доступа к базе домена.

Дополнительные материалы

На момент написания статьи разработчики WebKit планировали поддержку DOM Storage, но в nightly build ее не было.

Opera

На момент написания статьи Opera 9.5 (beta) не поддерживает ни DOM Storage ни Database Storage.

С другой стороны, разработчики планируют эту поддержку включить.

Flash. SharedObject.

Там, где нет DOM Storage, для offline-хранения используют flash-интерфейс SharedObject. Он позволяет хранить самые разные объекты средствами Adobe Flash.

Пример ActionScript для работы с SharedObject:

// создать/получить namespace storage
storage = SharedObject.getLocal("storage"); 
 
// записать данные name => Вася

storage.data.name="Вася";
 
// сохранить объект
storage.flush()
 
// перечислить свойства объекта
for (var name in storage.data) {

    trace(name + ":" + storage.data[name])
}

Чтобы работать с этим хранилищем из javascript, нужен способ коммуникации JS <->Flash.

В старых версиях Flash вызвать javascript можно через getURL('javascript:...').

Передать значение во Flash можно установкой переменной flash-объекту. Эту переменную flash-ролик может считывать каждый кадр и предпринимать соответствующие действия.

Во Flash 8+ появился интерфейс ExternalInterface, который позволяет как указывать AS-функцию для приема данных из JS, так и напрямую вызывать JS-метод.

Открыть рабочий пример передачи значения Flash <-> JS.

Код примера в ActionScript:

import flash.external.*;
 
// установить местную функцию recieveFromJS для приема данных

// от javascript-функции sendFromJS
ExternalInterface.addCallback("sendFromJS", null, recieveFromJS);
 
// Эта функция будет реагировать на sendFromJS
function recieveFromJS(text) {

    _root.theText.text = text; // .. и устанавливать текст в окошке
}
 
// Это действие, наоборот, отправляет данные в JS.
_root.button.onRelease = function() {

    // вызвать javascript-функцию recieveFromFlash
    ExternalInterface.call("recieveFromFlash", _root.theText.text);
    _root.theText.text = "";

}

Код примера в JS:

function recieveFromFlash(Txt) {

    document.getElementById('text').value = Txt;
}
 
function sendFromJS() {
    var value = document.getElementById('text').value

 
    var movie = (navigator.appName.indexOf("Microsoft")!=-1 ? window : document)["BridgeMovie"]    
 

    movie.sendFromJS(value);    
 
    document.getElementById('text').value = ''
}
Скачать исходники
Документация на ExternalInterface
Особенности и ограничения

Доступ к SharedObject ограничен роликами с того же домена.

Это принципиально отличается от Javascript, в котором доступ определяется адресом страницы а не скрипта, и делает возможным разного рода кросс-доменные трюки.

Ограничение по умолчанию на размер данных - в районе 100Kb, пользователь может уменьшить или увеличить его в специальном Flash-интерфейсе, который открывается при вызове ActionScript:

System.showSettings(1);
Проблемы реализации

Во-первых, надо иметь Flash. Хранилище доступно только после инициализации Flash-ролика.

Много ошибок в различных версиях Flash затрагивают ExternalInterface, включая повреждение данных во время передачи JS->Flash.

Проще всего узнать о них:

Много работы над обходом багов провел Brad Neuberg для flash-хранилища в dojo:

Резюме.

DOM Storage и аналогичные системы хранения - важный шаг к offline-работе web-приложений.

В браузере Opera все еще приходится использовать Flash Shared Object, а Firefox, Internet Explorer, Safari реализуют хранилище без дополительных плагинов и Flash<->JS коммуникации.

Интересным сектором для интеграции могут быть CRM-системы с нестабильным интернетом. Например, мини-точка продаж, в которой основная работа идет через интеренет, но хранение текущих договоров дублируется на рабочей машине.

Offline-хранилище может быть использовано для сохранения сложных состояний интерфейса - размеров окон, контрольных элементов и т.п.

Все изменения интерфейса посетителем мгновенно сохранятся в DOM Storage и восстановятся при следующем заходе на страницу без дополнительных механизмов сохранения интерфейса на сервере.

При этом объем хранимого состояния интерфейса вряд ли превысит ограничение в сотни килобайт. Идеальный объект для DOM Storage, не правда ли ?..


Об авторе:

Илья Кантор, в разработке интернет-приложений с 1999 года. Начиная с 2003 года разрабатывает клиентские интерфейсы и сложные поисковые структуры для баз данных.

В свободное время поддерживает полезные ресурсы, посвященные алгоритмам и методам программирования и разработке на клиенте: Firebug, XmlHttpRequest и др.

Новости мира IT:

Архив новостей

Последние комментарии:

IT-консалтинг Software Engineering Программирование СУБД Безопасность Internet Сети Операционные системы Hardware

Информация для рекламодателей PR-акции, размещение рекламы — adv@citforum.ru,
тел. +7 985 1945361
Пресс-релизы — pr@citforum.ru
Обратная связь
Информация для авторов
Rambler's Top100 TopList liveinternet.ru: показано число просмотров за 24 часа, посетителей за 24 часа и за сегодня This Web server launched on February 24, 1997
Copyright © 1997-2000 CIT, © 2001-2015 CIT Forum
Внимание! Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав. Подробнее...