Как я могу позволить пользователям эффективно сохранять содержимое хранилища объектов indexedDB в файл?

Я храню большое количество мелких объектов в IndexedDB. Я хотел бы дать пользователю возможность экспортировать одно из хранилищ объектов в файл, который они могут «скачать».

Я прочитал эта статья в блоге. Который описывает чтение данных, JSON.stringifyобработку данных, кодирование их с помощью encodeURIComponent и размещение их как href для ссылки, которую они могут использовать для загрузки данных. Что-то вроде этого:

var transaction = db.transaction([objectstore], "readonly");
var content = [];
var objectStore = transaction.objectStore(objectstore);

objectStore.openCursor().onsuccess = function(event) {
    var cursor = event.target.result;
    if (cursor) {
        content.push({key:cursor.key,value:cursor.value});
        cursor.continue();
    }
}; 

transaction.oncomplete = function(event) {
    var serializedData = JSON.stringify(dataToStore);
    link.attr("href",'data:Application/octet-stream,'+encodeURIComponent(serializedData));
    link.trigger("click");
};

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


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

var transaction = db.transaction([objectstore], "readonly");
var objectStore = transaction.objectStore(objectstore);

objectStore.getAll().onsuccess = function(evt) {
    var url = window.URL.createObjectURL(new Blob(evt.target.results, {'type': 'application/octet-stream'}));
    link.attr('href', url);
    link.trigger('click');
};

Что даст мне такие результаты, как:

  • 10 тыс. записей, среднее время экспорта 975,87 мс
  • 100 тыс. записей, среднее время экспорта 5850,10 мс
  • 1 миллион записей, среднее время экспорта 56 681,00 мс

Как видите, экспорт 1 миллиона записей занимает около минуты. Есть ли лучший способ сделать это? (Я также пытался использовать курсор вместо .getAll(), но курсоры работают медленнее)


person Chad    schedule 21.01.2013    source источник
comment
Я хочу сказать, используйте localStorage для просматриваемого в данный момент ресурса (и для сохранения/загрузки) и поместите БД в WebWorker, но я чувствую, что даже это не сильно повлияет на производительность.   -  person Jeffrey Sweeney    schedule 21.01.2013
comment
Да, я думал о том, чтобы выполнить загрузку и сериализацию из веб-воркера, но браузер все равно должен сериализовать его, чтобы вернуться; который, я думаю, получит такой же удар по производительности. Что касается локального хранилища, я не думаю, что помещать туда более 3 миллионов объектов - хорошая идея...   -  person Chad    schedule 21.01.2013
comment
ты пробовал? получение из 3 миллионов объектов из IndexedDB должно занять всего несколько секунд. Создание файла через window.URL.createObjectURL(new Blob(contents, {'type': MIME_TYPE})) должно быть в порядке.   -  person Kyaw Tun    schedule 21.01.2013
comment
Я играю с ним прямо сейчас, я не знал о методе window.URL.createObjectURL(new Blob());, который может ответить на мой вопрос, поскольку он кажется примерно в два раза быстрее, чем при использовании метода encodeURI. ранние скамейки говорят о 6 секундах для 100k записей.   -  person Chad    schedule 21.01.2013
comment
@KyawTun Похоже, что с 1 миллионом записей экспорт в файл в среднем занимает около одной минуты. Гораздо быстрее, чем при использовании encodeURI, но все же довольно медленно...   -  person Chad    schedule 21.01.2013
comment
Я понимаю. Я думаю, что это то, как далеко вы можете пойти. Для совместимости вы будете использовать курсор и нажать на ArrayBuffer.   -  person Kyaw Tun    schedule 22.01.2013
comment
Я думаю, вы правы, опубликуйте это как ответ, и я приму его.   -  person Chad    schedule 22.01.2013


Ответы (1)


IDBObjectStore.getAll не является частью стандарта IndexedDB и использует курсор под одеялом.

Примечание: Mozilla также реализовала getAll() для обработки этого случая (и getAllKeys(), которая в настоящее время скрыта за параметром dom.indexedDB.experimental в about:config). они не являются частью стандарта IndexedDB, поэтому могут исчезнуть в будущем. Мы включили их, потому что считаем их полезными. Следующий код делает то же самое, что и выше:

objectStore.getAll().onsuccess = function(event) {
  alert("Got all customers: " + event.target.result);
};

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

Единственный способ получить записи, где вы не знаете ключ, — это использовать курсор. Так что нет, я не думаю, что есть лучший способ. Но вам нужно спросить себя, быстрее ли это, чем получение записей с сервера.

person denov    schedule 20.02.2014
comment
Верно, но это не отвечает на первоначальный вопрос о том, как быстрее всего выполнить то, что я хочу сделать. - person Chad; 21.02.2014
comment
Я верю, что есть только один способ. Я не вижу других вариантов в API. - person denov; 25.02.2014
comment
Правильно, это автономное приложение, поэтому сервера нет. getAll было решением, которое я использовал, я действительно искал, есть ли что-то еще. - person Chad; 25.02.2014
comment
если нет сервера, как вы собираетесь защитить своих пользователей от очистки кеша или чего-либо еще, что может привести к потере данных? - person denov; 26.02.2014
comment
Опять же, это автономное приложение. Сохраненные данные предназначены только для пользователя, они могут делать с ними все, что хотят, включая удаление. - person Chad; 26.02.2014
comment
Да, я понимаю. Я не уверен, что лучше всего хранить данные только в браузере. Я думаю, что пользователю было бы слишком легко все испортить и все потерять. И только нахождение в браузере не позволяет им менять машины или браузеры. только мой .02 - person denov; 26.02.2014
comment
К счастью, вопрос не о системе в целом, поскольку я не дал достаточно информации о том, что она делает, чтобы кто-то мог ее обсудить. Я просто спрашиваю, есть ли метод сериализации, который был бы быстрее, чем у меня; и я оставляю вопрос открытым на случай, если появится решение. - person Chad; 26.02.2014
comment
@Чад, тебе повезло? Я столкнулся в основном с той же проблемой, и теперь getAll даже больше не существует. Поэтому я адаптировал его, используя openCursor, но все, что я получаю, это [object object] на blob... :( - person cregox; 30.11.2015
comment
@Cawas Нет, к сожалению, мне это не очень понравилось. Если бы я сделал это снова, я бы, вероятно, сериализовал свои данные как TypedArray и просто изменил бы это; к сожалению, единственный способ предложить его для скачивания по-прежнему window.URL.createObjectURL(), что невероятно медленно :/ - person Chad; 30.11.2015