Teste de cota de tamanho de armazenamento da Web

Para uma introdução ao armazenamento na Web, veja meu post anterior .

Confira a versão de produção no GitHub !

O armazenamento na Web é um recurso chave em meu projeto atual, que envolve armazenamento em buffer de vídeo. Mas cada navegador não suporta a mesma quantidade de armazenamento da web e alguns até diferem na quantidade de localStorage e sessionStorage disponível.

Um site agrega dados com base em cada navegador para fornecer uma linha de base para cada navegador. Esta é uma informação útil, mas tenho sérias reservas sobre como usá-la em um ambiente de produção. Principalmente porque a detecção de agente de usuário do lado do cliente é, na melhor das hipóteses, falha e há uma abundância de navegadores e versões diferentes. A única opção real era desenvolver um teste rápido para cada navegador, ou viver perigoso e lidar com as exceções levantadas ao adicionar muitos dados (para navegadores não-IE). Encontrei algumas bibliotecas, mas esperar 2 segundos para determinar o tamanho do cache seria inaceitável. Então eu escrevi meu próprio, diminuindo o tempo de execução em 10 vezes.

Primeiro, eu determino se o navegador é o IE. Novamente, a detecção do agente do usuário não é o melhor meio. O IE é o único navegador que oferece suporte a .remainingSpace e não lança quando o tamanho é excedido. Este bool é importante para uma porta lógica ao adicionar ao Web Storage.

//remaining space is only available in MSIE
var isIE = localStorage.remainingSpace !== 'undefined';

Em seguida, a matriz de strings de teste deve ser criada. Essas strings têm comprimentos diferentes e evitam uma penalidade dispendiosa de cópia de string devido à concatenação.

//create the array of different sized objects. hold the length to prevent costly calls to array length property
var arr = buildArray();
var length = arr.length;

function buildArray()
{
//build array, these will be the values that will be added to the web storage
var b1 = "0";
var b10 = increaseLength(b1, 10);
var b100 = increaseLength(b10, 10);
var kb1 = increaseLength(b100, 10);
var kb10 = increaseLength(kb1, 10);
var kb100 = increaseLength(kb10, 10);
var mb1 = increaseLength(kb100, 10);
var mb10 = increaseLength(mb1, 10);

//return array of various sizes, ordered smallest to largest
return [b1, b10, b100, kb1, kb10, kb100, mb1, mb10];
}

function increaseLength(string, times)
{
var temp = [];
while (times--)
temp
.push(string);
return temp.join('');
}

Agora o teste pode começar, esta versão testa os três sabores de Web Storage. Alguns navegadores terão cotas diferentes para localStorage e sessionStorage. globalStorage está em grande parte obsoleto e foi substituído por localStorage. Os resultados são postados em uma estrutura HTML construída de maneira grosseira.

//array of the types of Web Storage
var storage_types = ["localStorage", "sessionStorage", "globalStorage"];

//check for web storage
for (i = 0; i < storage_types.length; i++)
{
//verify the browser supports this form of web storage
if (supportsStorage(storage_types[i]))
{
//start the stopwatch to time how long the test takes
var sw = new StopWatch();
sw
.start();

//clear everything from web storage before starting
window
[storage_types[i]].clear();

//iterate to find the maximum amount of data that can be stored
checkSize
(storage_types[i]);

//stop the stopwatch
sw
.stop();

//print the results
document
.getElementById(storage_types[i] + 'support').innerHTML = storage_types[i] + " is supported by your browser over this protocol";
document
.getElementById(storage_types[i] + 'results').innerHTML = storageSize(storage_types[i]) + " Bytes available for " + storage_types[i] + " on this browser";
document
.getElementById(storage_types[i] + 'time').innerHTML = sw.elapsedTime + " [ms] to test browser " + storage_types[i];

//clear everything from web storage before starting next iteration (Firefox fix)
window
[storage_types[i]].clear();
}
else
{
//print the results
document
.getElementById(storage_types[i] + 'support').innerHTML = storage_types[i] + " is not supported by your browser over this protocol";
document
.getElementById(storage_types[i] + 'results').innerHTML = "N/A";
document
.getElementById(storage_types[i] + 'time').innerHTML = "N/A";
}
}

A função de verificação de tamanho é onde está a otimização real. Ele itera sobre a matriz de strings de teste, começando pela maior primeiro. Como o Web Storage será acionado se a cota de tamanho for excedida, começar com um valor grande é vantajoso para consumir grandes quantidades de espaço rapidamente. Ele será adicionado ao armazenamento da Web até exceder o tamanho e, em seguida, passará para uma unidade menor. Ele faz isso até que não haja espaço no armazenamento da Web para adicionar até mesmo outro byte. Como alguns navegadores permitem uma cota de armazenamento “ilimitada”, deve haver algo que impeça um loop infinito. Contar um iterador é menos caro do que verificar o comprimento do objeto Web Storage por meio de JSON.stringify , portanto, após 2 adições da string de 10 MB, podemos considerar isso “ilimitado”.

function checkSize(type)
{
//create a key for each storage entry
var iterator = 0;

//count the iterations for each entry, this will be used for the "unlimited" cases, which would cause an infinite loop
//the iterator counter eliminates the need to stringify the entire storage on each iteration and will break at ~20MB
var arr_iterator = 0;

//iterate over the array, from largest object to smallest
for (j = length - 1; j >= 0; j--)
{
//reset array iterator
arr_iterator
= 0;

//iterate until the data can no longer be added to the the web storage
while (addData(type, iterator++, arr[j]))
{
//increment the iterator
arr_iterator
++;

//if we have added ~20MB, then this is considered "unlimited"
if (j == length - 1 && arr_iterator >= 2)
return;
}
}
}

O método addData não verifica o tamanho atual (fora do IE, que não lança) e apenas lida com o erro de tamanho em excesso se ocorrer.

function addData(type, key, data)
{
//add data to new key, or replace value if the key already exists with new data
try
{
//error isn't thrown in MSIE
if (isIE)
{
if (getRemainingSpace(type) - data.length <= 0)
return false;
}

//add the value to the key, equivalent to localStorage.setItem('key', 'value')
window
[type][key] = data;
return true;
}
catch (e)
{
return false;
}
}

Os métodos auxiliares restantes: getRemaininSpace é usado para verificar o tamanho no IE. O tamanho do armazenamento utiliza JSON.stringify para obter o tamanho do objeto Web Storage. E StopWatch conta o número de milissegundos para executar o teste.

function getRemainingSpace(type)
{
//return the number of bytes still available in localStorage
if (isIE)
//only IE supports this function
return window[type].remainingSpace;
}

function storageSize(type)
{
//use stringify to determine the size of the web storage object
return JSON.stringify(window[type]).length;
}

function StopWatch()
{
//object that will be used to time our tests
this.startTime = false;
this.stopTime = false;
this.elapsedTime = false;

this.start = function()
{
this.startTime = new Date().getTime();
}
this.stop = function()
{
this.stopTime = new Date().getTime();
this.elapsedTime = this.stopTime - this.startTime;
}
}

Este teste é executado (na minha máquina) em menos de 200 ms em todos os navegadores que testei. Os resultados são iguais aos que encontrei no Web Storage Support Test . A verdadeira vantagem aqui é a velocidade na qual ele reduz o tamanho do cache e evita propriedades caras ou restringe o dicionário.

Lições importantes!

* Não é necessário jQuery.

* O IE não será lançado quando a cota de tamanho for excedida, mas oferece uma útil função de espaço restante (janela [{storageType}]. RestanteSpace).

* O Firefox compartilha localStorage e sessionStorage, com isso quero dizer que se você tiver 4 MB em localStorage, 1 MB estará disponível para sessionStorage. Outros navegadores permitem o duplo mergulho.

* Alguns navegadores permitem sessionStorage ilimitada, Safari (pelo menos para Windows) é um desses navegadores.

* A detecção do Agente do Usuário nem sempre é confiável. Verificar os recursos geralmente é a melhor opção. Para este aplicativo específico, apenas o IE teve um comportamento estranho.