Ir para o conteúdo
HexaTransfer
Voltar ao blog
Analises tecnicas

Browser Armazenamento APIs para Transferência de arquivos Applications

Leverage IndexedDB, File System Access API, e Cache API para arquivo transfer apps. Armazenamento limits, performance, e browser compatibility.

O armazenamento no browser para uma aplicação de transferência de ficheiros divide-se claramente por quatro APIs: IndexedDB para metadados de sessão e fragmentos estruturados (transacional, assíncrono, com tipos), a File System Access API para ler e escrever ficheiros de vários gigabytes diretamente no disco do utilizador, a Cache API para respostas HTTP e recursos da shell da aplicação, e a Storage Manager API para gestão de quotas e indicações de persistência. O Origin Private File System (OPFS) complementa estas para I/O de alto desempenho em sandbox. Escolher a certa é importante porque os limites variam de 1 GB no iOS Safari a "60% do disco livre" no Chrome para desktop, e a escolha errada acaba por originar QuotaExceededError em produção.

Mapear Cada API ao Trabalho Certo

Use o IndexedDB para tudo o que se assemelhe a uma linha de tabela: sessões de carregamento, índices de fragmentos concluídos, metadados de partilha, tokens de revogação. É assíncrono, transacional, indexável e persiste entre sessões.

Use a File System Access API quando precisar de escrever bytes no disco sem carregar o ficheiro inteiro para a RAM — ideal para guardar transferências desencriptadas com mais de 500 MB. Chrome, Edge e Opera suportam-na; Firefox e Safari implementam apenas um subconjunto de leitura via showOpenFilePicker.

Use a Cache API para objetos de resposta HTTP — o bundle JS, CSS, ícones e eventualmente respostas de API em cache. Está otimizada para intercepção de fetch em Service Workers.

Use o OPFS (um ramo Origin-Private especial da File System Access API) quando pretender armazenamento rápido, em sandbox e não visível pelo utilizador — por exemplo, um buffer de escrita durante uma passagem de cifração de vários gigabytes. Atinge um débito para disco uma ordem de grandeza superior ao IndexedDB para blobs binários.

IndexedDB Sem os Aspetos Problemáticos

O IndexedDB direto tem uma API baseada em eventos notoriamente estranha. Use o pacote idb de Jake Archibald (1,5 KB comprimido) ou o Dexie.js (20 KB, API de consulta mais rica):

import { openDB } from 'idb';
const db = await openDB('transfers', 2, {
  upgrade(db, oldVersion) {
    if (oldVersion < 1) {
      const sessions = db.createObjectStore('sessions', { keyPath: 'id' });
      sessions.createIndex('by_expiry', 'expiresAt');
    }
    if (oldVersion < 2) {
      db.createObjectStore('chunks', { keyPath: ['sessionId', 'index'] });
    }
  }
});
await db.put('sessions', { id: 'abc', fileName: 'report.pdf', expiresAt: Date.now() + 86400000 });

As migrações de versão correm no callback upgrade. Proteja sempre as migrações com oldVersion para que utilizadores que saltem da v1 para a v3 obtenham ambos os passos.

O IndexedDB processa a maioria das formas de dados, incluindo Blobs e referências de File, através de structured clone. Isso significa que pode armazenar um handle de File num registo de sessão e reler os bytes do ficheiro original após o recarregamento do separador — perfeito para carregamentos retomáveis.

File System Access API para Transferências de Grande Dimensão

A API permite associar um stream gravável à caixa de diálogo de gravação do browser:

const handle = await window.showSaveFilePicker({
  suggestedName: 'decrypted-archive.zip',
  types: [{ description: 'Zip', accept: { 'application/zip': ['.zip'] } }]
});
const writable = await handle.createWritable();
await decryptionStream.pipeTo(writable);

Os bytes fluem diretamente para o disco, sem aterrar no heap do JS. Esta é a única forma prática de guardar um ficheiro desencriptado de 10 GB no browser.

Para Firefox e Safari, recorra ao StreamSaver.js como alternativa, que usa um Service Worker para sintetizar uma resposta em streaming que aciona a interface de transferência. Ergonomia idêntica, com algumas partes móveis a mais.

Os handles de ficheiro persistentes também permitem que uma aplicação reabra ficheiros entre sessões. Uma vez que o utilizador conceda permissão via showOpenFilePicker, pode persistir o FileSystemFileHandle no IndexedDB e posteriormente chamar handle.requestPermission() para recuperar o acesso sem re-solicitar permissão para cada ficheiro.

OPFS para Espaço de Trabalho Temporário

O Origin Private File System é um armazenamento em sandbox por origem que se comporta como um sistema de ficheiros mas não é visível para o utilizador:

const root = await navigator.storage.getDirectory();
const fh = await root.getFileHandle('scratch.bin', { create: true });
const access = await fh.createSyncAccessHandle(); // apenas workers
access.write(buffer, { at: offset });
access.flush();
access.close();

O createSyncAccessHandle está disponível apenas dentro de Web Workers (incluindo Service Workers). É síncrono e extremamente rápido — os benchmarks mostram 3 a 10x o desempenho do IndexedDB para escritas sequenciais. Use-o para colocar em buffer algumas centenas de megabytes de saída de cifração antes do carregamento, ou para guardar em cache uma cópia de trabalho desencriptada sem poluir a pasta de Transferências do utilizador.

O Safari 17 lançou o OPFS com handles de acesso síncronos; o Firefox 111 seguiu-se. Os três principais browsers suportam-no agora, o que o torna viável para código em produção.

Quotas de Armazenamento e Como Sobreviver-lhes

Todas as APIs partilham o mesmo pool de quotas de origem. Limites aproximados:

  • Chrome para desktop: 60% do disco livre
  • Firefox para desktop: 50% do disco livre, limitado a 2 GB por origem por defeito
  • Safari para desktop: aviso em 1 GB, cresce até ~20% do disco com aprovação do utilizador
  • iOS Safari: 1 GB por origem, evicção agressiva ao fim de 7 dias sem utilização
  • Chrome para Android: 10% do disco livre, evicção sob pressão

Verifique a quota em tempo de execução:

const { quota, usage } = await navigator.storage.estimate();
console.log(`Using ${(usage/1e9).toFixed(2)} GB of ${(quota/1e9).toFixed(2)} GB`);

Solicite persistência para armazenamentos críticos:

const persisted = await navigator.storage.persist();

Devolve true se o browser conceder armazenamento persistente, o que significa que não será evictado sob pressão. O Chrome concede isto automaticamente a sites com os quais o utilizador interagiu significativamente; o Firefox solicita confirmação.

Cache API para Shell da Aplicação e Modo Offline

A Cache API armazena pares Request + Response e é a escolha certa dentro de Service Workers:

const cache = await caches.open('shell-v7');
await cache.addAll([
  '/', '/app.js', '/app.css', '/icons/192.png'
]);

Recuperar na intercepção:

self.addEventListener('fetch', (e) => {
  e.respondWith(caches.match(e.request).then(r => r ?? fetch(e.request)));
});

Não coloque bytes de ficheiros cifrados na Cache API. Um objeto de resposta de 2 GB ultrapassa a quota do iOS Safari de uma só vez e não pode ser acedido por intervalo depois. Os bytes pertencem ao OPFS ou diretamente para o disco via File System Access.

Gerir Evicção e Perda de Dados com Graciosidade

O armazenamento não persistente é evictado — tem de se planear para isso. O iOS Safari evicta ao fim de 7 dias sem utilização, independentemente da quota. O Chrome evicta apenas quando o disco está realmente sob pressão. O Firefox evicta as origens menos recentemente utilizadas quando o pool de quotas enche.

Dois padrões defensivos:

  • Escreva qualquer estado que não possa recriar (IDs de sessão de carregamento, desvios de fragmentos parciais) num formato amigo de recarregamento, para que um novo carregamento de página possa re-obter do servidor e continuar.
  • Para estado de longa duração, solicite navigator.storage.persist() e apresente uma interface ao utilizador para confirmar quando o browser solicitar.

Mantenha o servidor como fonte de verdade para tudo o que não se possa perder. Trate o armazenamento no browser como uma cache rápida que pode desaparecer de um dia para o outro.

Armadilhas de Compatibilidade entre Browsers

Três armadilhas surgem repetidamente:

  1. A enumeração indexedDB.databases() não é suportada no Firefox (utilizadores com a opção "eliminar cookies ao fechar" perdem todo o conteúdo do IndexedDB sem emitir eventos).
  2. FileSystemFileHandle.queryPermission() comporta-se de forma diferente após recarregamentos — por vezes devolve 'prompt' mesmo quando a permissão foi concedida. Chame sempre requestPermission() de forma defensiva.
  3. O modo privado/anónimo atribui a todas as três APIs uma quota separada, menor e apenas de sessão. Código que funciona em navegação normal pode atingir imediatamente QuotaExceededError em janelas privadas.

O HexaTransfer usa IndexedDB para o estado de sessão, OPFS para o buffer de texto cifrado durante a cifração em streaming, e a File System Access API para transferências desencriptadas de 10 GB nos browsers suportados. Experimente em hexatransfer.com — gratuito, sem conta necessária, máximo de 10 GB.

Escolher uma Pilha para a Sua Aplicação

Para a maioria das aplicações de transferência, a combinação certa é: wrapper idb sobre IndexedDB para metadados, handles de acesso síncrono do OPFS para espaço de trabalho de cifração/desencriptação, Cache API para shell da aplicação dentro de um Service Worker, File System Access API para transferências finais com alternativa StreamSaver, e uma chamada navigator.storage.persist() durante o onboarding. Isso cobre todos os browsers atualmente em circulação, mantém-se dentro das quotas em dispositivos móveis e recupera com graciosidade quando algo é evictado. Construa adaptadores pequenos à volta de cada API para que, quando o OPFS lançar um novo método ou o Safari aumentar a sua quota, altere um único ficheiro e publique.

Envie arquivos grandes com segurança e criptografia de ponta a ponta

Transfira arquivos de até 10 GB gratuitamente com criptografia de ponta a ponta. Sem necessidade de conta. Seus arquivos são criptografados no navegador antes do envio — ninguém mais pode lê-los.

Enviar um arquivo