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
Filesystemtrabalha 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/shareSincronize com as plataformas nativas:
- Ouça o áudio com a tela desligada
- Ganhe Certificado após a conclusão
- + de 5000 cursos para você explorar!
Baixar o aplicativo
npx cap syncImporte 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/csvda 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
| Problema | Causa provável | Como tratar |
|---|---|---|
| Falha ao ler arquivo | Arquivo não existe ou caminho/diretório incorreto | Verifique com readdir ou trate exceção e ofereça recriar/exportar novamente |
| Compartilhamento não abre | URI inválida ou ambiente não suporta (ex: web) | Cheque plataforma e use fallback (download no navegador) quando rodar como PWA |
| Conteúdo corrompido | Codificação incorreta | Para 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).