Texto longo, leia o resumo
O Chrome 61, com mais navegadores a seguir, agora mostra uma estimativa de quanto de armazenamento um app da Web está usando e quanto está disponível por meio de:
if ('storage' in navigator && 'estimate' in navigator.storage) {
navigator.storage.estimate().then(({usage, quota}) => {
console.log(`Using ${usage} out of ${quota} bytes.`);
});
}
Apps da Web modernos e armazenamento de dados
Quando você pensa nas necessidades de armazenamento de um aplicativo da Web moderno, é útil dividir o que está sendo armazenado em duas categorias: os dados principais necessários para carregar o aplicativo da Web e os dados necessários para uma interação significativa do usuário depois que o aplicativo é carregado.
O primeiro tipo de dados, o que é necessário para carregar seu app da Web, consiste em HTML,
JavaScript, CSS e talvez algumas imagens. Os service workers, junto com a API Cache Storage, fornecem a infraestrutura necessária para salvar esses recursos principais e usá-los mais tarde para carregar rapidamente seu app da Web, idealmente ignorando a rede por completo.
Ferramentas que se integram ao processo de build do seu app da Web, como as novas
bibliotecas Workbox ou as mais antigas
sw-precache
,
podem automatizar totalmente o processo de armazenamento, atualização e uso desse tipo de
dados.
Mas e os outros tipos de dados? Esses são recursos que não são necessários para carregar seu app da Web, mas que podem ter um papel crucial na experiência geral do usuário. Se você estiver escrevendo um app da Web para edição de imagens, por exemplo, talvez seja necessário salvar uma ou mais cópias locais de uma imagem, permitindo que os usuários alternem entre revisões e desfaçam o trabalho. Ou, se você estiver desenvolvendo uma experiência de reprodução de mídia off-line, salvar arquivos de áudio ou vídeo localmente seria um recurso essencial. Todo app da Web que pode ser personalizado acaba precisando salvar algum tipo de informações de estado. Como saber quanto espaço está disponível para esse tipo de armazenamento de execução e o que acontece quando você fica sem espaço?
O passado: window.webkitStorageInfo
e navigator.webkitTemporaryStorage
Os navegadores sempre ofereceram suporte a esse tipo de introspecção por meio de interfaces
prefixadas, como a muito antiga (e descontinuada)
window.webkitStorageInfo
,
e a não tão antiga, mas ainda não padrão
navigator.webkitTemporaryStorage
.
Embora essas interfaces forneçam informações úteis, elas não têm um
futuro como padrões da Web.
É aí que o padrão de armazenamento WHATWG entra em cena.
O futuro: navigator.storage
Como parte do trabalho em andamento no Storage Living Standard, algumas APIs úteis foram
adicionadas à interface
StorageManager
, que é exposta aos navegadores como
navigator.storage
.
Como muitas outras APIs da Web mais recentes, a navigator.storage
está disponível apenas em origens seguras
(servidas por HTTPS ou localhost).
No ano passado, lançamos o método
navigator.storage.persist()
, que permite que seu app da Web solicite que o armazenamento seja
excluído da limpeza automática.
Agora, ele é acompanhado pelo método navigator.storage.estimate()
, que serve como uma
substituição moderna para navigator.webkitTemporaryStorage.queryUsageAndQuota()
.
estimate()
retorna informações semelhantes, mas expõe uma interface
baseada em promessas,
que é compatível com outras APIs assíncronas modernas. A promessa que
estimate()
retorna é resolvida com um objeto que contém duas propriedades: usage
,
que representa o número de bytes usados no momento, e quota
, que representa o
número máximo de bytes que podem ser armazenados pela
origem atual.
Como tudo relacionado ao armazenamento, a cota é aplicada em toda a
origem.
Se um aplicativo da Web tentar armazenar dados grandes o suficiente para exceder a cota disponível de uma determinada origem usando, por exemplo, o IndexedDB ou a API Cache Storage, a solicitação vai falhar com uma exceção
QuotaExceededError
.
Estimativas de armazenamento em ação
A forma exata de usar estimate()
depende do tipo de dados que o app precisa
armazenar. Por exemplo, é possível atualizar um controle na interface para informar aos usuários
quanto espaço está sendo usado após a conclusão de cada operação de armazenamento.
O ideal é fornecer uma interface que permita aos usuários limpar manualmente os dados
que não são mais necessários. Você pode escrever um código como este:
// For a primer on async/await, see
// https://developers.google.com/web/fundamentals/getting-started/primers/async-functions
async function storeDataAndUpdateUI(dataUrl) {
// Pro-tip: The Cache Storage API is available outside of service workers!
// See https://googlechrome.github.io/samples/service-worker/window-caches/
const cache = await caches.open('data-cache');
await cache.add(dataUrl);
if ('storage' in navigator && 'estimate' in navigator.storage) {
const {usage, quota} = await navigator.storage.estimate();
const percentUsed = Math.round(usage / quota * 100);
const usageInMib = Math.round(usage / (1024 * 1024));
const quotaInMib = Math.round(quota / (1024 * 1024));
const details = `${usageInMib} out of ${quotaInMib} MiB used (${percentUsed}%)`;
// This assumes there's a <span id="storageEstimate"> or similar on the page.
document.querySelector('#storageEstimate').innerText = details;
}
}
Qual é a precisão da estimativa?
É difícil não perceber que os dados que você recebe da função são apenas
uma estimativa do espaço que uma origem está usando. Está no nome da
função. Nem os valores usage
nem quota
são estáveis. Portanto, é recomendável considerar o seguinte:
usage
reflete quantos bytes uma determinada origem está usando efetivamente para dados de mesma origem, que, por sua vez, podem ser afetados por técnicas de compactação interna, blocos de alocação de tamanho fixo que podem incluir espaço não utilizado e a presença de registros"tombstone" que podem ser criados temporariamente após uma exclusão. Para evitar o vazamento de informações de tamanho exato, recursos opacos de origem cruzada salvos localmente podem contribuir com bytes de preenchimento adicionais para o valorusage
geral.quota
reflete a quantidade de espaço reservada atualmente para uma origem. O valor depende de alguns fatores constantes, como o tamanho geral do armazenamento, mas também de vários fatores potencialmente voláteis, incluindo a quantidade de espaço de armazenamento que não está em uso no momento. Portanto, à medida que outros aplicativos em um dispositivo gravam ou excluem dados, a quantidade de espaço que o navegador está disposto a dedicar à origem do app da Web provavelmente vai mudar.
O presente: detecção de recursos e substitutos
estimate()
está ativado por padrão a partir do Chrome 61. O Firefox está
testando o navigator.storage
, mas, desde agosto de 2017, ele não é ativado
por padrão. É necessário
ativar a preferência dom.storageManager.enabled
para testá-la.
Ao trabalhar com funcionalidades que ainda não têm suporte em todos os navegadores,
a detecção de recursos é obrigatória. É possível combinar a detecção de recursos com um
wrapper baseado em promessas sobre os métodos navigator.webkitTemporaryStorage
mais antigos para fornecer uma interface consistente, como:
function storageEstimateWrapper() {
if ('storage' in navigator && 'estimate' in navigator.storage) {
// We've got the real thing! Return its response.
return navigator.storage.estimate();
}
if ('webkitTemporaryStorage' in navigator &&
'queryUsageAndQuota' in navigator.webkitTemporaryStorage) {
// Return a promise-based wrapper that will follow the expected interface.
return new Promise(function(resolve, reject) {
navigator.webkitTemporaryStorage.queryUsageAndQuota(
function(usage, quota) {resolve({usage: usage, quota: quota})},
reject
);
});
}
// If we can't estimate the values, return a Promise that resolves with NaN.
return Promise.resolve({usage: NaN, quota: NaN});
}