API File System Access: simplificação do acesso a arquivos locais

A API File System Access permite que apps da Web leiam ou salvem mudanças diretamente em arquivos e pastas no dispositivo do usuário.

O que é a API File System Access?

A API File System Access permite que os desenvolvedores criem apps da Web poderosos que interagem com arquivos no dispositivo local do usuário, como IDEs, editores de fotos e vídeos, editores de texto e muito mais. Depois que um usuário concede acesso a um app da Web, essa API permite que ele leia ou salve alterações diretamente em arquivos e pastas no dispositivo do usuário. Além de ler e gravar arquivos, a API File System Access oferece a capacidade de abrir um diretório e enumerar o conteúdo dele.

Se você já trabalhou com leitura e gravação de arquivos, grande parte do que vou compartilhar será familiar para você. Recomendo que você leia mesmo assim, porque nem todos os sistemas são iguais.

A API File System Access é compatível com a maioria dos navegadores Chromium no Windows, macOS, ChromeOS e Linux. Uma exceção notável é o Brave, que está disponível apenas com uma flag. O suporte para Android está sendo trabalhado no contexto de crbug.com/1011535.

Como usar a API File System Access

Para mostrar o poder e a utilidade da API File System Access, escrevi um editor de texto de arquivo único. Ele permite abrir um arquivo de texto, editá-lo, salvar as alterações no disco ou iniciar um novo arquivo e salvar as alterações no disco. Não é nada sofisticado, mas é suficiente para ajudar você a entender os conceitos.

Suporte ao navegador

Compatibilidade com navegadores

  • Chrome: 86.
  • Edge: 86.
  • Firefox: não é compatível.
  • Safari: não é compatível.

Origem

Detecção de recursos

Para saber se a API File System Access tem suporte, verifique se o método de seletor que você quer usar existe.

if ('showOpenFilePicker' in self) {
  // The `showOpenFilePicker()` method of the File System Access API is supported.
}

Testar

Confira a API File System Access em ação na demonstração do editor de texto.

Ler um arquivo do sistema de arquivos local

O primeiro caso de uso que quero abordar é pedir que o usuário escolha um arquivo e, em seguida, abra e leia esse arquivo do disco.

Pedir que o usuário escolha um arquivo para ler

O ponto de entrada da API File System Access é window.showOpenFilePicker(). Quando chamada, ela mostra uma caixa de diálogo do seletor de arquivos e solicita que o usuário selecione um arquivo. Depois que o usuário seleciona um arquivo, a API retorna uma matriz de identificadores de arquivos. Um parâmetro options opcional permite influenciar o comportamento do seletor de arquivos, por exemplo, permitindo que o usuário selecione vários arquivos, diretórios ou tipos de arquivos diferentes. Sem opções especificadas, o seletor de arquivos permite que o usuário selecione um único arquivo. Isso é perfeito para um editor de texto.

Como muitas outras APIs, a chamada de showOpenFilePicker() precisa ser feita em um contexto seguro e ser chamada em um gesto do usuário.

let fileHandle;
butOpenFile.addEventListener('click', async () => {
  // Destructure the one-element array.
  [fileHandle] = await window.showOpenFilePicker();
  // Do something with the file handle.
});

Quando o usuário seleciona um arquivo, showOpenFilePicker() retorna uma matriz de identificadores, neste caso, uma matriz de um elemento com um FileSystemFileHandle que contém as propriedades e os métodos necessários para interagir com o arquivo.

É útil manter uma referência ao identificador de arquivo para que ele possa ser usado mais tarde. Ela será necessária para salvar alterações no arquivo ou realizar outras operações.

Ler um arquivo do sistema de arquivos

Agora que você tem um identificador de um arquivo, é possível acessar as propriedades dele ou o próprio arquivo. Por enquanto, vou ler o conteúdo. Chamar handle.getFile() retorna um objeto File, que contém um blob. Para extrair os dados do blob, chame um dos métodos dele (slice(), stream(), text() ou arrayBuffer()).

const file = await fileHandle.getFile();
const contents = await file.text();

O objeto File retornado por FileSystemFileHandle.getFile() só pode ser lido se o arquivo no disco não tiver mudado. Se o arquivo no disco for modificado, o objeto File vai ficar ilegível, e você vai precisar chamar getFile() novamente para receber um novo objeto File e ler os dados alterados.

Como tudo funciona em conjunto

Quando os usuários clicam no botão Open, o navegador mostra um seletor de arquivos. Depois que o usuário seleciona um arquivo, o app lê o conteúdo e o coloca em um <textarea>.

let fileHandle;
butOpenFile.addEventListener('click', async () => {
  [fileHandle] = await window.showOpenFilePicker();
  const file = await fileHandle.getFile();
  const contents = await file.text();
  textArea.value = contents;
});

Gravar o arquivo no sistema de arquivos local

No editor de texto, há duas maneiras de salvar um arquivo: Salvar e Salvar como. Salvar grava as alterações no arquivo original usando o identificador de arquivo recuperado anteriormente. No entanto, Salvar como cria um novo arquivo e, portanto, exige um novo identificador de arquivo.

Criar um novo arquivo

Para salvar um arquivo, chame showSaveFilePicker(), que mostra o seletor de arquivos no modo "salvar", permitindo que o usuário escolha um novo arquivo para salvar. Para o editor de texto, também queria que ele adicionasse automaticamente uma extensão .txt. Por isso, forneci alguns parâmetros adicionais.

async function getNewFileHandle() {
  const options = {
    types: [
      {
        description: 'Text Files',
        accept: {
          'text/plain': ['.txt'],
        },
      },
    ],
  };
  const handle = await window.showSaveFilePicker(options);
  return handle;
}

Salvar alterações no disco

Você pode encontrar todo o código para salvar alterações em um arquivo na minha demonstração do editor de texto no GitHub. As interações principais do sistema de arquivos estão em fs-helpers.js. Na forma mais simples, o processo fica parecido com o código a seguir. Vou explicar cada etapa.

// fileHandle is an instance of FileSystemFileHandle..
async function writeFile(fileHandle, contents) {
  // Create a FileSystemWritableFileStream to write to.
  const writable = await fileHandle.createWritable();
  // Write the contents of the file to the stream.
  await writable.write(contents);
  // Close the file and write the contents to disk.
  await writable.close();
}

A gravação de dados no disco usa um objeto FileSystemWritableFileStream, uma subclasse de WritableStream. Crie o fluxo chamando createWritable() no objeto de identificador de arquivo. Quando createWritable() é chamado, o navegador primeiro verifica se o usuário concedeu permissão de gravação ao arquivo. Se a permissão de gravação não tiver sido concedida, o navegador vai solicitar a permissão ao usuário. Se a permissão não for concedida, o createWritable() vai gerar uma DOMException, e o app não vai conseguir gravar no arquivo. No editor de texto, os objetos DOMException são processados no método saveFile().

O método write() recebe uma string, que é o que é necessário para um editor de texto. Mas também pode usar um BufferSource ou um Blob. Por exemplo, é possível canalizar um stream diretamente para ele:

async function writeURLToFile(fileHandle, url) {
  // Create a FileSystemWritableFileStream to write to.
  const writable = await fileHandle.createWritable();
  // Make an HTTP request for the contents.
  const response = await fetch(url);
  // Stream the response into the file.
  await response.body.pipeTo(writable);
  // pipeTo() closes the destination pipe by default, no need to close it.
}

Também é possível usar seek() ou truncate() no stream para atualizar o arquivo em uma posição específica ou redimensioná-lo.

Como especificar um nome de arquivo sugerido e um diretório inicial

Em muitos casos, você pode querer que o app sugira um nome de arquivo ou local padrão. Por exemplo, um editor de texto pode sugerir um nome de arquivo padrão de Untitled Text.txt em vez de Untitled. Para fazer isso, transmita uma propriedade suggestedName como parte das opções showSaveFilePicker.

const fileHandle = await self.showSaveFilePicker({
  suggestedName: 'Untitled Text.txt',
  types: [{
    description: 'Text documents',
    accept: {
      'text/plain': ['.txt'],
    },
  }],
});

O mesmo vale para o diretório de início padrão. Se você estiver criando um editor de texto, talvez seja melhor iniciar a caixa de diálogo de salvamento ou abertura de arquivos na pasta documents padrão. Já para um editor de imagens, talvez seja melhor iniciar na pasta pictures padrão. É possível sugerir um diretório de início padrão transmitindo uma propriedade startIn para os métodos showSaveFilePicker, showDirectoryPicker() ou showOpenFilePicker.

const fileHandle = await self.showOpenFilePicker({
  startIn: 'pictures'
});

A lista de diretórios de sistema conhecidos é:

  • desktop: o diretório de área de trabalho do usuário, se ele existir.
  • documents: diretório em que os documentos criados pelo usuário normalmente são armazenados.
  • downloads: diretório em que os arquivos salvos são armazenados.
  • music: diretório em que os arquivos de áudio normalmente são armazenados.
  • pictures: diretório em que fotos e outras imagens estáticas normalmente são armazenadas.
  • videos: diretório em que vídeos ou filmes normalmente são armazenados.

Além dos diretórios do sistema conhecidos, também é possível transmitir um identificador de arquivo ou diretório existente como um valor para startIn. A caixa de diálogo seria aberta no mesmo diretório.

// Assume `directoryHandle` is a handle to a previously opened directory.
const fileHandle = await self.showOpenFilePicker({
  startIn: directoryHandle
});

Como especificar o propósito de diferentes seletores de arquivos

Às vezes, os aplicativos têm seletores diferentes para finalidades diferentes. Por exemplo, um editor de texto rico pode permitir que o usuário abra arquivos de texto, mas também importe imagens. Por padrão, cada seletor de arquivos era aberto no último local lembrado. Você pode contornar esse problema armazenando valores id para cada tipo de seletor. Se um id for especificado, a implementação do seletor de arquivos vai lembrar um diretório separado usado pela última vez para esse id.

const fileHandle1 = await self.showSaveFilePicker({
  id: 'openText',
});

const fileHandle2 = await self.showSaveFilePicker({
  id: 'importImage',
});

Como armazenar identificadores de arquivo ou de diretório na IndexedDB

Os identificadores de arquivos e de diretórios são serializáveis, o que significa que você pode salvar um identificador de arquivo ou diretório no IndexedDB ou chamar postMessage() para enviá-los entre a mesma origem de nível superior.

Salvar identificadores de arquivo ou diretório no IndexedDB significa que você pode armazenar o estado ou lembrar de quais arquivos ou diretórios um usuário estava trabalhando. Isso permite manter uma lista de arquivos abertos ou editados recentemente, oferecer a reabertura do último arquivo quando o app for aberto, restaurar o diretório de trabalho anterior e muito mais. No editor de texto, eu armazeno uma lista dos cinco arquivos mais recentes que o usuário abriu, permitindo que ele acesse esses arquivos novamente.

O exemplo de código a seguir mostra como armazenar e recuperar um identificador de arquivo e um identificador de diretório. Você pode ver isso em ação no Glitch. Para encurtar, usei a biblioteca idb-keyval.

import { get, set } from 'https://unpkg.com/idb-keyval@5.0.2/dist/esm/index.js';

const pre1 = document.querySelector('pre.file');
const pre2 = document.querySelector('pre.directory');
const button1 = document.querySelector('button.file');
const button2 = document.querySelector('button.directory');

// File handle
button1.addEventListener('click', async () => {
  try {
    const fileHandleOrUndefined = await get('file');
    if (fileHandleOrUndefined) {
      pre1.textContent = `Retrieved file handle "${fileHandleOrUndefined.name}" from IndexedDB.`;
      return;
    }
    const [fileHandle] = await window.showOpenFilePicker();
    await set('file', fileHandle);
    pre1.textContent = `Stored file handle for "${fileHandle.name}" in IndexedDB.`;
  } catch (error) {
    alert(error.name, error.message);
  }
});

// Directory handle
button2.addEventListener('click', async () => {
  try {
    const directoryHandleOrUndefined = await get('directory');
    if (directoryHandleOrUndefined) {
      pre2.textContent = `Retrieved directroy handle "${directoryHandleOrUndefined.name}" from IndexedDB.`;
      return;
    }
    const directoryHandle = await window.showDirectoryPicker();
    await set('directory', directoryHandle);
    pre2.textContent = `Stored directory handle for "${directoryHandle.name}" in IndexedDB.`;
  } catch (error) {
    alert(error.name, error.message);
  }
});

Permissões e identificadores de arquivos ou diretórios armazenados

Como as permissões nem sempre são mantidas entre as sessões, verifique se o usuário concedeu permissão ao arquivo ou diretório usando queryPermission(). Se não, chame requestPermission() para solicitar (ou solicitar novamente). Isso funciona da mesma forma para identificadores de arquivos e diretórios. Você precisa executar fileOrDirectoryHandle.requestPermission(descriptor) ou fileOrDirectoryHandle.queryPermission(descriptor), respectivamente.

No editor de texto, criei um método verifyPermission() que verifica se o usuário já concedeu a permissão e, se necessário, faz a solicitação.

async function verifyPermission(fileHandle, readWrite) {
  const options = {};
  if (readWrite) {
    options.mode = 'readwrite';
  }
  // Check if permission was already granted. If so, return true.
  if ((await fileHandle.queryPermission(options)) === 'granted') {
    return true;
  }
  // Request permission. If the user grants permission, return true.
  if ((await fileHandle.requestPermission(options)) === 'granted') {
    return true;
  }
  // The user didn't grant permission, so return false.
  return false;
}

Ao solicitar a permissão de gravação com a solicitação de leitura, reduzi o número de solicitações de permissão. O usuário recebe uma solicitação ao abrir o arquivo e concede permissão para leitura e gravação.

Abrir um diretório e enumerar o conteúdo dele

Para enumerar todos os arquivos em um diretório, chame showDirectoryPicker(). O usuário seleciona um diretório em um seletor, após o qual um FileSystemDirectoryHandle é retornado, o que permite enumerar e acessar os arquivos do diretório. Por padrão, você terá acesso de leitura aos arquivos no diretório, mas, se precisar de acesso de gravação, transmita { mode: 'readwrite' } para o método.

butDir.addEventListener('click', async () => {
  const dirHandle = await window.showDirectoryPicker();
  for await (const entry of dirHandle.values()) {
    console.log(entry.kind, entry.name);
  }
});

Se você também precisar acessar cada arquivo usando getFile() para, por exemplo, extrair os tamanhos de arquivo individuais, não use await em cada resultado sequencialmente, mas processe todos os arquivos em paralelo, por exemplo, usando Promise.all().

butDir.addEventListener('click', async () => {
  const dirHandle = await window.showDirectoryPicker();
  const promises = [];
  for await (const entry of dirHandle.values()) {
    if (entry.kind !== 'file') {
      continue;
    }
    promises.push(entry.getFile().then((file) => `${file.name} (${file.size})`));
  }
  console.log(await Promise.all(promises));
});

Criar ou acessar arquivos e pastas em um diretório

Em um diretório, é possível criar ou acessar arquivos e pastas usando o método getFileHandle() ou, respectivamente, o método getDirectoryHandle(). Ao transmitir um objeto options opcional com uma chave create e um valor booleano de true ou false, é possível determinar se um novo arquivo ou pasta precisa ser criado se ele não existir.

// In an existing directory, create a new directory named "My Documents".
const newDirectoryHandle = await existingDirectoryHandle.getDirectoryHandle('My Documents', {
  create: true,
});
// In this new directory, create a file named "My Notes.txt".
const newFileHandle = await newDirectoryHandle.getFileHandle('My Notes.txt', { create: true });

Como resolver o caminho de um item em um diretório

Ao trabalhar com arquivos ou pastas em um diretório, pode ser útil resolver o caminho do item em questão. Isso pode ser feito com o método resolve(). Para resolução, o item pode ser um filho direto ou indireto do diretório.

// Resolve the path of the previously created file called "My Notes.txt".
const path = await newDirectoryHandle.resolve(newFileHandle);
// `path` is now ["My Documents", "My Notes.txt"]

Como excluir arquivos e pastas em um diretório

Se você tiver acesso a um diretório, poderá excluir os arquivos e pastas contidos nele com o método removeEntry(). Para pastas, a exclusão pode ser recursiva e incluir todas as subpastas e os arquivos nelas contidos.

// Delete a file.
await directoryHandle.removeEntry('Abandoned Projects.txt');
// Recursively delete a folder.
await directoryHandle.removeEntry('Old Stuff', { recursive: true });

Excluir um arquivo ou pasta diretamente

Se você tiver acesso a um identificador de arquivo ou diretório, chame remove() em um FileSystemFileHandle ou FileSystemDirectoryHandle para removê-lo.

// Delete a file.
await fileHandle.remove();
// Delete a directory.
await directoryHandle.remove();

Renomear e mover arquivos e pastas

Os arquivos e pastas podem ser renomeados ou movidos para um novo local chamando move() na interface FileSystemHandle. FileSystemHandle tem as interfaces filhas FileSystemFileHandle e FileSystemDirectoryHandle. O método move() usa um ou dois parâmetros. O primeiro pode ser uma string com o novo nome ou um FileSystemDirectoryHandle para a pasta de destino. No segundo caso, o segundo parâmetro opcional é uma string com o novo nome, para que a movimentação e a renomeação possam acontecer em uma etapa.

// Rename the file.
await file.move('new_name');
// Move the file to a new directory.
await file.move(directory);
// Move the file to a new directory and rename it.
await file.move(directory, 'newer_name');

Integração de arrastar e soltar

As interfaces de arrastar e soltar do HTML permitem que aplicativos da Web aceitem arquivos arrastados e soltos em uma página da Web. Durante uma operação de arrastar e soltar, os itens de arquivo e diretório arrastados são associados a entradas de arquivo e de diretório, respectivamente. O método DataTransferItem.getAsFileSystemHandle() retorna uma promessa com um objeto FileSystemFileHandle se o item arrastado for um arquivo e uma promessa com um objeto FileSystemDirectoryHandle se o item arrastado for um diretório. A lista a seguir mostra isso em ação. A interface de arrastar e soltar é DataTransferItem.kind para e diretórios de arquivos, enquanto FileSystemHandle.kind da API File System Access é "file" para arquivos e "directory" para diretórios."file"

elem.addEventListener('dragover', (e) => {
  // Prevent navigation.
  e.preventDefault();
});

elem.addEventListener('drop', async (e) => {
  e.preventDefault();

  const fileHandlesPromises = [...e.dataTransfer.items]
    .filter((item) => item.kind === 'file')
    .map((item) => item.getAsFileSystemHandle());

  for await (const handle of fileHandlesPromises) {
    if (handle.kind === 'directory') {
      console.log(`Directory: ${handle.name}`);
    } else {
      console.log(`File: ${handle.name}`);
    }
  }
});

Como acessar o sistema de arquivos particular de origem

O sistema de arquivos particular de origem é um endpoint de armazenamento que, como o nome sugere, é particular para a origem da página. Embora os navegadores normalmente implementem isso mantendo o conteúdo desse sistema de arquivos particular de origem no disco em algum lugar, não é esperado que o conteúdo seja acessível ao usuário. Da mesma forma, não é esperado que arquivos ou diretórios com nomes que correspondam aos nomes de filhos do sistema de arquivos particular de origem existam. Embora o navegador possa fazer parecer que há arquivos, internamente, como se fosse um sistema de arquivos particular de origem, ele pode armazenar esses "arquivos" em um banco de dados ou qualquer outra estrutura de dados. Essencialmente, se você usar essa API, não espere encontrar os arquivos criados correspondentes um a um em algum lugar do disco rígido. Você pode operar normalmente no sistema de arquivos particular de origem depois de ter acesso à FileSystemDirectoryHandle raiz.

const root = await navigator.storage.getDirectory();
// Create a new file handle.
const fileHandle = await root.getFileHandle('Untitled.txt', { create: true });
// Create a new directory handle.
const dirHandle = await root.getDirectoryHandle('New Folder', { create: true });
// Recursively remove a directory.
await root.removeEntry('Old Stuff', { recursive: true });

Compatibilidade com navegadores

  • Chrome: 86.
  • Edge: 86.
  • Firefox: 111.
  • Safari: 15.2.

Origem

Como acessar arquivos otimizados para desempenho no sistema de arquivos particular de origem

O sistema de arquivos particular de origem oferece acesso opcional a um tipo especial de arquivo altamente otimizado para desempenho, por exemplo, oferecendo acesso de gravação no local e exclusivo ao conteúdo de um arquivo. No Chromium 102 e versões mais recentes, há um método adicional no sistema de arquivos particular de origem para simplificar o acesso a arquivos: createSyncAccessHandle() (para operações de leitura e gravação síncronas). Ele é exposto em FileSystemFileHandle, mas exclusivamente em Web Workers.

// (Read and write operations are synchronous,
// but obtaining the handle is asynchronous.)
// Synchronous access exclusively in Worker contexts.
const accessHandle = await fileHandle.createSyncAccessHandle();
const writtenBytes = accessHandle.write(buffer);
const readBytes = accessHandle.read(buffer, { at: 1 });

Polipreenchimento

Não é possível usar o polyfill completamente nos métodos da API File System Access.

  • O método showOpenFilePicker() pode ser aproximado com um elemento <input type="file">.
  • O método showSaveFilePicker() pode ser simulado com um elemento <a download="file_name">, mas isso aciona um download programático e não permite a substituição de arquivos existentes.
  • O método showDirectoryPicker() pode ser emulado de certa forma com o elemento <input type="file" webkitdirectory> não padrão.

Desenvolvemos uma biblioteca chamada browser-fs-access, que usa a API File System Access sempre que possível e que usa as próximas melhores opções em todos os outros casos.

Segurança e permissões

A equipe do Chrome projetou e implementou a API File System Access usando os princípios básicos definidos em Como controlar o acesso a recursos poderosos da plataforma da Web, incluindo o controle e a transparência do usuário, além da ergonomia do usuário.

Abrir ou salvar um arquivo

Seletor de arquivos para abrir um arquivo para leitura
Um seletor de arquivos usado para abrir um arquivo existente para leitura.

Ao abrir um arquivo, o usuário concede permissão para ler um arquivo ou diretório usando o seletor de arquivos. O seletor de arquivos aberto só pode ser mostrado usando um gesto do usuário quando servido em um contexto seguro. Se os usuários mudarem de ideia, eles podem cancelar a seleção no seletor de arquivos, e o site não terá acesso a nada. Esse é o mesmo comportamento do elemento <input type="file">.

Seletor de arquivos para salvar um arquivo no disco.
Um seletor de arquivos usado para salvar um arquivo no disco.

Da mesma forma, quando um app da Web quer salvar um novo arquivo, o navegador mostra o seletor de arquivos salvos, permitindo que o usuário especifique o nome e o local do novo arquivo. Como eles estão salvando um novo arquivo no dispositivo (em vez de substituir um arquivo existente), o seletor de arquivos concede ao app a permissão para gravar no arquivo.

Pastas restritas

Para ajudar a proteger os usuários e os dados deles, o navegador pode limitar a capacidade de salvar em determinadas pastas, por exemplo, pastas principais do sistema operacional, como o Windows e as pastas da biblioteca do macOS. Quando isso acontece, o navegador mostra uma solicitação e pede que o usuário escolha uma pasta diferente.

Como modificar um arquivo ou diretório

Um app da Web não pode modificar um arquivo no disco sem a permissão explícita do usuário.

Solicitação de permissão

Se uma pessoa quiser salvar alterações em um arquivo para o qual concedeu acesso de leitura anteriormente, o navegador vai mostrar uma solicitação de permissão, solicitando permissão para que o site grave as alterações no disco. A solicitação de permissão só pode ser acionada por um gesto do usuário, por exemplo, clicando em um botão "Salvar".

Solicitação de permissão mostrada antes de salvar um arquivo.
Solicitação mostrada aos usuários antes que o navegador receba permissão de gravação em um arquivo existente.

Como alternativa, um app da Web que edita vários arquivos, como um ambiente de desenvolvimento integrado (IDE, na sigla em inglês), também pode solicitar permissão para salvar as alterações no momento da abertura.

Se o usuário escolher "Cancelar" e não conceder acesso de gravação, o app da Web não poderá salvar as alterações no arquivo local. Ele precisa oferecer um método alternativo para o usuário salvar os dados, por exemplo, disponibilizando uma maneira de "fazer o download" do arquivo ou salvar dados na nuvem.

Transparência

Ícone da Omnibox
Ícone da barra de endereço indicando que o usuário concedeu ao site permissão para salvar em um arquivo local.

Depois que um usuário concede permissão a um app da Web para salvar um arquivo local, o navegador mostra um ícone na barra de endereço. Ao clicar no ícone, um pop-up é aberto mostrando a lista de arquivos a que o usuário tem acesso. O usuário pode revogar esse acesso a qualquer momento.

Persistência de permissões

O app da Web pode continuar salvando as alterações no arquivo sem solicitar até que todas as guias da origem sejam fechadas. Quando uma guia é fechada, o site perde todo o acesso. Na próxima vez que o usuário usar o app da Web, ele vai receber uma nova solicitação de acesso aos arquivos.

Feedback

Queremos saber sobre sua experiência com a API File System Access.

Conte sobre o design da API

Há algo na API que não funciona como esperado? Ou há métodos ou propriedades ausentes que você precisa para implementar sua ideia? Tem dúvidas ou comentários sobre o modelo de segurança?

Problemas com a implementação?

Você encontrou um bug na implementação do Chrome? Ou a implementação é diferente da especificação?

  • Registre um bug em https://new.crbug.com. Inclua o máximo de detalhes possível, instruções para reprodução e defina Components como Blink>Storage>FileSystem. O Glitch é ótimo para compartilhar reprosagens rápidas.

Planeja usar a API?

Você planeja usar a API File System Access no seu site? Seu apoio público nos ajuda a priorizar os recursos e mostra a outros fornecedores de navegadores a importância de oferecer suporte a eles.

Links úteis

Agradecimentos

A especificação da API File System Access foi escrita por Marijn Kruisselbrink.