Skip to content
Aprender Medir Blog Case studies Sobre
Nesta página
  • A maneira tradicional de lidar com arquivos
    • Abrindo arquivos
    • Abrindo diretórios
    • Salvando (em vez de baixando) arquivos
    • O problema
  • A API de acesso ao sistema de arquivos
    • Abrindo arquivos
    • Abrindo diretórios
    • Salvando arquivos
  • Apresentando o navegador-fs-access
    • Filosofia de design
    • Usando a biblioteca browser-fs-access
    • Demo
  • A biblioteca browser-fs-access em liberdade
    • Amostra de código de aplicação real
    • Considerações de interface do usuário
  • Conclusões
  • Reconhecimentos
  • Home
  • All articles

Ler e gravar arquivos e diretórios com a biblioteca browser-fs-access

Jul 27, 2020 — Atualizado Jan 25, 2022
Available in: Español, English
Thomas Steiner
Thomas Steiner
TwitterGitHubGlitchHomepage
Nesta página
  • A maneira tradicional de lidar com arquivos
    • Abrindo arquivos
    • Abrindo diretórios
    • Salvando (em vez de baixando) arquivos
    • O problema
  • A API de acesso ao sistema de arquivos
    • Abrindo arquivos
    • Abrindo diretórios
    • Salvando arquivos
  • Apresentando o navegador-fs-access
    • Filosofia de design
    • Usando a biblioteca browser-fs-access
    • Demo
  • A biblioteca browser-fs-access em liberdade
    • Amostra de código de aplicação real
    • Considerações de interface do usuário
  • Conclusões
  • Reconhecimentos

Os navegadores já conseguem lidar com arquivos e diretórios há muito tempo. A API Arquivo fornece recursos para representar objetos de arquivo em aplicativos da web, bem como selecioná-los programaticamente e acessar seus dados. No momento em que você olha mais de perto, porém, nem tudo o que reluz é ouro.

A maneira tradicional de lidar com arquivos #

Se você sabe como funcionava do jeito antigo, pode pular direto para o novo jeito.

Abrindo arquivos #

Como desenvolvedor, você pode abrir e ler arquivos por meio do elemento <input type="file">. Em sua forma mais simples, abrir um arquivo pode ser parecido com o exemplo de código abaixo. O objeto de input FileList, que no caso a seguir consiste em apenas um File. Um File é um tipo específico de Blob e pode ser usado em qualquer contexto em que um Blob.

const openFile = async () => {
return new Promise((resolve) => {
const input = document.createElement('input');
input.type = 'file';
input.addEventListener('change', () => {
resolve(input.files[0]);
});
input.click();
});
};

Abrindo diretórios #

Para abrir pastas (ou diretórios), você pode definir o atributo <input webkitdirectory> Tirando isso, todo o resto funciona da mesma forma que acima. Apesar de seu nome com o prefixo do fornecedor, webkitdirectory não pode ser usado apenas nos navegadores Chromium e WebKit, mas também no Edge baseado em EdgeHTML legado e no Firefox.

Salvando (em vez de baixando) arquivos #

Para salvar um arquivo, tradicionalmente, você está limitado a baixar um arquivo, o que funciona graças ao atributo <a download> Dado um Blob, você pode definir o href da âncora como um blob: URL que pode ser obtido no método URL.createObjectURL()

Cuidado

Para evitar vazamentos de memória, sempre revogue o URL após o download.

const saveFile = async (blob) => {
const a = document.createElement('a');
a.download = 'my-file.txt';
a.href = URL.createObjectURL(blob);
a.addEventListener('click', (e) => {
setTimeout(() => URL.revokeObjectURL(a.href), 30 * 1000);
});
a.click();
};

O problema #

Uma grande desvantagem da abordagem de download é que não há como fazer um fluxo clássico abrir → editar → salvar, ou seja, não há como sobrescrever o arquivo original. Em vez disso, você acaba com uma nova cópia do arquivo original na pasta de Downloads padrão do sistema operacional sempre que "salva".

A API de acesso ao sistema de arquivos #

A API de acesso ao sistema de arquivos torna ambas as operações, abrir e salvar, muito mais simples. Também possibilita o salvamento real, ou seja, você não só pode escolher onde salvar um arquivo, mas também sobrescrever um arquivo existente.

Para obter uma introdução mais completa à API de acesso ao sistema de arquivos, consulte o artigo A API de acesso ao sistema de arquivos: simplificando o acesso a arquivos locais.

Abrindo arquivos #

Com a API de acesso ao sistema de arquivos, abrir um arquivo é uma questão de chamar o método window.showOpenFilePicker(). Esta chamada retorna um identificador de arquivo, do qual você pode obter o File real por meio do método getFile().

const openFile = async () => {
try {
// Always returns an array.
const [handle] = await window.showOpenFilePicker();
return handle.getFile();
} catch (err) {
console.error(err.name, err.message);
}
};

Abrindo diretórios #

Abra um diretório chamando window.showDirectoryPicker() que torna os diretórios selecionáveis na caixa de diálogo do arquivo.

Salvando arquivos #

Salvar arquivos é igualmente simples. A partir de um identificador de arquivo, você cria um fluxo gravável por meio de createWritable(), depois grava os dados Blob chamando o write() do fluxo e, por fim, fecha o fluxo chamando seu método close().

const saveFile = async (blob) => {
try {
const handle = await window.showSaveFilePicker({
types: [{
accept: {
// Omitted
},
}],
});
const writable = await handle.createWritable();
await writable.write(blob);
await writable.close();
return handle;
} catch (err) {
console.error(err.name, err.message);
}
};

Apresentando o navegador-fs-access #

Por mais perfeita que seja a API de acesso ao sistema de arquivos, ela ainda não está amplamente disponível.

Tabela de suporte do navegador para a API de acesso ao sistema de arquivos. Todos os navegadores são marcados como 'sem suporte' ou 'atrás de um sinalizador'.
Tabela de suporte do navegador para a API de acesso ao sistema de arquivos. (Fonte)

É por isso que vejo a API de acesso ao sistema de arquivos como um aprimoramento progressivo. Como tal, quero usá-lo quando o navegador oferecer suporte e, se não for, usar a abordagem tradicional; ao mesmo tempo, nunca pune o usuário com downloads desnecessários de código JavaScript não suportado. A biblioteca browser-fs-access é minha resposta para esse desafio.

Filosofia de design #

Como a API de acesso ao sistema de arquivos provavelmente ainda mudará no futuro, a API browser-fs-access não é modelada a partir dela. Ou seja, a biblioteca não é um polyfill, mas sim um ponyfill. Você pode (estaticamente ou dinamicamente) importar exclusivamente qualquer funcionalidade necessária para manter seu aplicativo o menor possível. Os métodos disponíveis são os nomeados apropriadamente fileOpen(), directoryOpen() e fileSave(). Internamente, o recurso de biblioteca detecta se a API de acesso ao sistema de arquivos é compatível e, a seguir, importa o caminho do código correspondente.

Usando a biblioteca browser-fs-access #

Os três métodos são intuitivos de usar. Você pode especificar os mimeTypes ou extensions arquivo aceitos do seu aplicativo e definir um multiple para permitir ou proibir a seleção de vários arquivos ou diretórios. Para obter detalhes completos, consulte a documentação da API browser-fs-access. O exemplo de código abaixo mostra como você pode abrir e salvar arquivos de imagem.

// The imported methods will use the File
// System Access API or a fallback implementation.
import {
fileOpen,
directoryOpen,
fileSave,
} from 'https://unpkg.com/browser-fs-access';

(async () => {
// Open an image file.
const blob = await fileOpen({
mimeTypes: ['image/*'],
});

// Open multiple image files.
const blobs = await fileOpen({
mimeTypes: ['image/*'],
multiple: true,
});

// Open all files in a directory,
// recursively including subdirectories.
const blobsInDirectory = await directoryOpen({
recursive: true
});

// Save a file.
await fileSave(blob, {
fileName: 'Untitled.png',
});
})();

Demo #

Você pode ver o código acima em ação em uma demonstração no Glitch. Seu código-fonte também está disponível lá. Como, por razões de segurança, os subquadros de origem cruzada não têm permissão para mostrar um seletor de arquivos, a demonstração não pode ser incorporada neste artigo.

A biblioteca browser-fs-access em liberdade #

Em meu tempo livre, contribuo um pouquinho para um PWA instalável chamado Excalidraw, uma ferramenta de quadro branco que permite esboçar diagramas facilmente com uma sensação de desenho à mão. É totalmente responsivo e funciona bem em uma variedade de dispositivos, desde pequenos telefones celulares a computadores com telas grandes. Isso significa que ele precisa lidar com arquivos em todas as várias plataformas, independentemente de serem ou não compatíveis com a API de acesso ao sistema de arquivos. Isso o torna um ótimo candidato para a biblioteca browser-fs-access.

Posso, por exemplo, iniciar um desenho no meu iPhone, salvá-lo (tecnicamente: baixe-o, pois o Safari não oferece suporte à API de acesso ao sistema de arquivos) na pasta Downloads do meu iPhone, abra o arquivo no meu desktop (após transferi-lo do meu telefone), modifique o arquivo e substitua-o com minhas alterações ou mesmo salve-o como um novo arquivo.

Um desenho Excalidraw em um iPhone.
Iniciando um desenho Excalidraw em um iPhone onde a API de acesso ao sistema de arquivos não é suportada, mas onde um arquivo pode ser salvo (baixado) na pasta Downloads.
O desenho Excalidraw modificado no Chrome na área de trabalho.
Abrindo e modificando o desenho Excalidraw na área de trabalho onde a API de acesso ao sistema de arquivos é suportada e, portanto, o arquivo pode ser acessado por meio da API.
Substituindo o arquivo original com as modificações.
Substituindo o arquivo original com as modificações no arquivo de desenho original Excalidraw. O navegador mostra uma caixa de diálogo perguntando se está tudo bem.
Salvando as modificações em um novo arquivo de desenho Excalidraw.
Salvando as modificações em um novo arquivo Excalidraw. O arquivo original permanece intocado.

Amostra de código de aplicação real #

Abaixo, você pode ver um exemplo real de navegador-fs-access como ele é usado no Excalidraw. Este trecho foi retirado de /src/data/json.ts. É de interesse especial como o saveAsJSON() passa um identificador de arquivo ou null para o método browser-fs-access ' fileSave(), o que faz com que ele seja sobrescrito quando um identificador é fornecido, ou salva em um novo arquivo se não for.

export const saveAsJSON = async (
elements: readonly ExcalidrawElement[],
appState: AppState,
fileHandle: any,

) => {
const serialized = serializeAsJSON(elements, appState);
const blob = new Blob([serialized], {
type: "application/json",
});
const name = `${appState.name}.excalidraw`;
(window as any).handle = await fileSave(
blob,
{
fileName: name,
description: "Excalidraw file",
extensions: ["excalidraw"],
},
fileHandle || null,
);
};

export const loadFromJSON = async () => {
const blob = await fileOpen({
description: "Excalidraw files",
extensions: ["json", "excalidraw"],
mimeTypes: ["application/json"],
});
return loadFromBlob(blob);
};

Considerações de interface do usuário #

Seja no Excalidraw ou em seu aplicativo, a IU deve se adaptar à situação de suporte do navegador. Se a API de acesso ao sistema de arquivos for suportada (if ('showOpenFilePicker' in window) {}), você pode mostrar um botão Salvar como além de um botão Salvar. As imagens abaixo mostram a diferença entre a barra de ferramentas responsiva do aplicativo principal do Excalidraw no iPhone e na área de trabalho do Chrome. Observe como no iPhone o botão Salvar como está faltando.

Excalidraw app toolbar no iPhone com apenas um botão 'Salvar'.
Barra de ferramentas do aplicativo Excalidraw no iPhone com apenas um botão Salvar.
Barra de ferramentas do aplicativo Excalidraw na área de trabalho do Chrome com um botão 'Salvar' e 'Salvar como'.
Barra de ferramentas do aplicativo Excalidraw no Chrome com um botão Salvar e um botão Salvar em foco.

Conclusões #

Trabalhar com arquivos de sistema funciona tecnicamente em todos os navegadores modernos. Em navegadores que suportam a API de acesso ao sistema de arquivos, você pode tornar a experiência melhor permitindo o verdadeiro salvamento e sobrescrita (não apenas o download) de arquivos e permitindo que seus usuários criem novos arquivos onde quiserem, ao mesmo tempo em que permanecem funcionais em navegadores que não suporta a API de acesso ao sistema de arquivos. O navegador-fs-access torna sua vida mais fácil, lidando com as sutilezas do aprimoramento progressivo e tornando seu código o mais simples possível.

Reconhecimentos #

Este artigo foi revisado por Joe Medley e Kayce Basques. Agradeço aos colaboradores da Excalidraw por seu trabalho no projeto e por revisar minhas solicitações de pull. Imagem do herói por Ilya Pavlov em Unsplash.

Aplicações Web ProgressivasFuncionalidades
Last updated: Jan 25, 2022 — Improve article
Return to all articles
Compartilhar
assinar

Contribute

  • Registrar um bug
  • Visualizar código-fonte

Conteúdo relacionado

  • developer.chrome.com
  • Atualizações do Chrome
  • Web Fundamentals
  • Estudos de caso
  • Podcasts
  • Shows

Conectar

  • Twitter
  • YouTube
  • Google Developers
  • Chrome
  • Firebase
  • Google Cloud Platform
  • Todos os produtos
  • Termos e privacidade
  • Diretrizes da comunidade

Except as otherwise noted, the content of this page is licensed under the Creative Commons Attribution 4.0 License, and code samples are licensed under the Apache 2.0 License. For details, see the Google Developers Site Policies.