Arquivos e compartilhamento no Ionic com Capacitor: leitura, escrita e exportação

Capítulo 16

Tempo estimado de leitura: 8 minutos

+ Exercício

O que muda ao trabalhar com arquivos no dispositivo

Quando você salva dados em Storage ou preferências, está lidando com valores pequenos e estruturados (chave/valor). Já arquivos (JSON, CSV, imagens, PDFs) exigem lidar com: diretórios, caminhos, codificação (texto vs binário), permissões e compartilhamento com outros apps. No Ionic com Capacitor, o caminho mais comum é usar o plugin @capacitor/filesystem para leitura/escrita e o plugin @capacitor/share para abrir o compartilhamento nativo do sistema.

Onde salvar: diretórios recomendados (Android/iOS)

Em geral, prefira diretórios do app (sandbox) para evitar permissões extras e garantir que o app sempre consiga ler/escrever. O Capacitor abstrai isso com Directory:

  • Directory.Data: dados do app (privado). Bom para arquivos que o usuário não precisa ver no gerenciador de arquivos.
  • Directory.Documents: documentos do app (pode ser mais apropriado para exportações, dependendo do fluxo e plataforma).
  • Directory.Cache: temporários (podem ser limpos pelo sistema).

Evite depender de caminhos absolutos (como /storage/emulated/0/...) porque variam por fabricante, versão do Android e políticas de armazenamento. Em iOS, o sandbox é ainda mais restrito e caminhos absolutos não são estáveis.

Permissões e limites (Android/iOS)

  • Android (scoped storage): versões recentes restringem acesso direto ao armazenamento “público”. Escrever em diretórios do app normalmente não exige permissão. Para acessar áreas públicas (Downloads, etc.), você pode precisar de abordagens específicas (SAF/Document Picker) ou plugins adicionais.
  • iOS: o app escreve no próprio sandbox sem permissões. Para “exportar”, o padrão é compartilhar/abrir o arquivo via sheet do sistema (Share Sheet) ou usar “Salvar em Arquivos” via UI do sistema.
  • Tamanho e memória: o Filesystem trabalha bem com texto (base64/utf-8). Para arquivos grandes, evite carregar tudo em memória; prefira gerar conteúdo de forma eficiente e, se necessário, usar soluções específicas para streaming (fora do escopo básico).

Instalação e configuração dos plugins

Em um projeto Ionic + Capacitor, instale os plugins necessários:

npm i @capacitor/filesystem @capacitor/share

Sincronize com as plataformas nativas:

Continue em nosso aplicativo e ...
  • Ouça o áudio com a tela desligada
  • Ganhe Certificado após a conclusão
  • + de 5000 cursos para você explorar!
ou continue lendo abaixo...
Download App

Baixar o aplicativo

npx cap sync

Importe no código quando for usar:

import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';
import { Share } from '@capacitor/share';

Leitura e escrita: operações essenciais com Filesystem

1) Criar uma pasta (organização de diretórios)

Organize seus arquivos em uma subpasta do app, por exemplo exports:

await Filesystem.mkdir({
  path: 'exports',
  directory: Directory.Data,
  recursive: true,
});

Se a pasta já existir, pode ocorrer erro. Uma prática comum é tentar criar e ignorar o erro, ou checar com readdir.

2) Escrever um arquivo de texto (JSON/CSV)

Para texto, use Encoding.UTF8. Exemplo salvando um JSON:

const jsonString = JSON.stringify({ createdAt: new Date().toISOString(), items: [1, 2, 3] }, null, 2);

await Filesystem.writeFile({
  path: 'exports/relatorio.json',
  data: jsonString,
  directory: Directory.Data,
  encoding: Encoding.UTF8,
});

3) Ler um arquivo de texto

const result = await Filesystem.readFile({
  path: 'exports/relatorio.json',
  directory: Directory.Data,
  encoding: Encoding.UTF8,
});

const content = result.data; // string
const parsed = JSON.parse(content as string);

4) Listar arquivos de uma pasta

const listing = await Filesystem.readdir({
  path: 'exports',
  directory: Directory.Data,
});

// listing.files contém nomes/entradas

5) Apagar arquivo

await Filesystem.deleteFile({
  path: 'exports/relatorio.json',
  directory: Directory.Data,
});

Entendendo caminhos: path relativo vs URI

Nas operações acima, usamos path relativo ao directory. Para compartilhar/abrir um arquivo, muitas vezes você precisa de uma URI/URL do arquivo. Para isso, use Filesystem.getUri:

const uriResult = await Filesystem.getUri({
  directory: Directory.Data,
  path: 'exports/relatorio.json',
});

const fileUri = uriResult.uri; // ex: file://... ou content://...

Essa URI é a forma mais segura de referenciar o arquivo no contexto do sistema operacional, sem depender de caminhos absolutos.

Caso prático: exportar dados como CSV e compartilhar

Vamos montar um fluxo completo: gerar um CSV a partir de uma lista, salvar em Directory.Data e abrir o compartilhamento nativo para enviar por e-mail, WhatsApp, salvar em arquivos, etc.

Passo 1) Funções utilitárias: gerar CSV

CSV parece simples, mas precisa escapar aspas e lidar com vírgulas/quebras de linha. Um gerador básico:

type Row = Record<string, any>;

function escapeCsv(value: any): string {
  const str = String(value ?? '');
  const mustQuote = /[",\n\r;]/.test(str) || str.includes(',');
  const escaped = str.replace(/"/g, '""');
  return mustQuote ? `"${escaped}"` : escaped;
}

function toCsv(rows: Row[], headers: string[]): string {
  const headerLine = headers.map(escapeCsv).join(',');
  const lines = rows.map(r => headers.map(h => escapeCsv(r[h])).join(','));
  return [headerLine, ...lines].join('\n');
}

Passo 2) Gerar conteúdo e salvar em arquivo

Exemplo de dados e escrita do arquivo:

const rows = [
  { id: 1, nome: 'Ana', total: 10.5, data: '2026-01-25' },
  { id: 2, nome: 'Bruno', total: 20, data: '2026-01-26' },
];

const headers = ['id', 'nome', 'total', 'data'];
const csv = toCsv(rows, headers);

await Filesystem.mkdir({
  path: 'exports',
  directory: Directory.Data,
  recursive: true,
});

const fileName = `export-${Date.now()}.csv`;
const filePath = `exports/${fileName}`;

await Filesystem.writeFile({
  path: filePath,
  directory: Directory.Data,
  data: csv,
  encoding: Encoding.UTF8,
});

Passo 3) Obter URI e compartilhar com o sistema

Agora pegue a URI e chame o Share Sheet:

const { uri } = await Filesystem.getUri({
  directory: Directory.Data,
  path: filePath,
});

await Share.share({
  title: 'Exportação CSV',
  text: 'Segue o arquivo CSV exportado do aplicativo.',
  url: uri,
  dialogTitle: 'Compartilhar arquivo',
});

Observações importantes:

  • Nem todo app receptor interpreta text/csv da mesma forma; o compartilhamento via URI costuma funcionar bem para “enviar arquivo”.
  • Se você precisar definir MIME type explicitamente, pode ser necessário um plugin complementar ou estratégia específica; o Share do Capacitor foca no fluxo padrão de compartilhamento.

Exemplo completo (serviço Angular) para exportar e compartilhar

Uma boa prática é centralizar a lógica em um serviço. Exemplo:

import { Injectable } from '@angular/core';
import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';
import { Share } from '@capacitor/share';

@Injectable({ providedIn: 'root' })
export class ExportService {
  private exportDir = 'exports';

  private escapeCsv(value: any): string {
    const str = String(value ?? '');
    const mustQuote = /[",\n\r]/.test(str) || str.includes(',');
    const escaped = str.replace(/"/g, '""');
    return mustQuote ? `"${escaped}"` : escaped;
  }

  private toCsv(rows: Record<string, any>[], headers: string[]): string {
    const headerLine = headers.map(h => this.escapeCsv(h)).join(',');
    const lines = rows.map(r => headers.map(h => this.escapeCsv(r[h])).join(','));
    return [headerLine, ...lines].join('\n');
  }

  async exportCsvAndShare(rows: Record<string, any>[], headers: string[]) {
    await Filesystem.mkdir({
      path: this.exportDir,
      directory: Directory.Data,
      recursive: true,
    });

    const csv = this.toCsv(rows, headers);
    const fileName = `export-${Date.now()}.csv`;
    const path = `${this.exportDir}/${fileName}`;

    await Filesystem.writeFile({
      path,
      directory: Directory.Data,
      data: csv,
      encoding: Encoding.UTF8,
    });

    const { uri } = await Filesystem.getUri({ directory: Directory.Data, path });

    await Share.share({
      title: 'Exportação CSV',
      text: 'Arquivo exportado do app.',
      url: uri,
      dialogTitle: 'Compartilhar',
    });

    return { fileName, uri };
  }
}

Uso em uma página

import { Component } from '@angular/core';
import { ExportService } from '../services/export.service';

@Component({
  selector: 'app-relatorios',
  template: `<ion-button (click)="exportar()">Exportar CSV</ion-button>`,
})
export class RelatoriosPage {
  constructor(private exportService: ExportService) {}

  async exportar() {
    const rows = [
      { id: 1, nome: 'Ana', total: 10.5, data: '2026-01-25' },
      { id: 2, nome: 'Bruno', total: 20, data: '2026-01-26' },
    ];
    const headers = ['id', 'nome', 'total', 'data'];

    await this.exportService.exportCsvAndShare(rows, headers);
  }
}

Tratamento de erros e boas práticas

Erros comuns e como lidar

ProblemaCausa provávelComo tratar
Falha ao ler arquivoArquivo não existe ou caminho/diretório incorretoVerifique com readdir ou trate exceção e ofereça recriar/exportar novamente
Compartilhamento não abreURI inválida ou ambiente não suporta (ex: web)Cheque plataforma e use fallback (download no navegador) quando rodar como PWA
Conteúdo corrompidoCodificação incorretaPara texto use Encoding.UTF8; para binário avalie base64 e plugins adequados

Dica: diferenciar Web (PWA) de Android/iOS

Se seu app também roda no navegador, o Filesystem pode se comportar diferente (armazenamento virtual). Para exportação em web, muitas vezes o melhor é gerar um Blob e disparar download. Em Android/iOS, o fluxo com writeFile + getUri + Share.share é o mais consistente.

Organização de nomes e versionamento

  • Inclua timestamp no nome do arquivo (Date.now()) para evitar sobrescrita.
  • Crie subpastas por tipo (exports/json, exports/csv) se o app gerar muitos arquivos.
  • Se o usuário exporta dados sensíveis, considere criptografar antes de salvar (exige abordagem adicional).

Agora responda o exercício sobre o conteúdo:

Ao implementar um fluxo de exportação e compartilhamento de um arquivo gerado no app (ex.: CSV) no Ionic com Capacitor, qual sequência de ações é a mais adequada para evitar caminhos absolutos e usar o compartilhamento nativo?

Você acertou! Parabéns, agora siga para a próxima página

Você errou! Tente novamente.

O fluxo consistente é salvar no sandbox do app (ex.: Directory.Data), gerar o arquivo com codificação correta e usar Filesystem.getUri para obter uma URI segura. Essa URI é então passada para Share.share, evitando depender de caminhos absolutos.

Próximo capitúlo

Qualidade e organização de código em projetos Ionic: padrões e estrutura por features

Arrow Right Icon
Capa do Ebook gratuito Ionic para Iniciantes: aplicativos híbridos com HTML, CSS e TypeScript
76%

Ionic para Iniciantes: aplicativos híbridos com HTML, CSS e TypeScript

Novo curso

21 páginas

Baixe o app para ganhar Certificação grátis e ouvir os cursos em background, mesmo com a tela desligada.