@kaymasoft/file-chooser
v0.0.3
Published
Plugin Capacitor para Android que abre o chooser nativo para seleção de arquivo ou captura de foto, corrigindo problemas recentes em WebView/inputs file e permitindo integração com apps Ionic Angular.
Readme
@kaymasoft/file-chooser
Plugin para Android que permite abrir o chooser nativo para seleção de arquivo ou captura de foto, integrando com apps Ionic Angular via Capacitor.
🚀 Visão Geral
Este plugin resolve problemas específicos do Android 14/15 onde os inputs <input type="file"> em WebViews não abrem o chooser nativo corretamente. Oferece uma solução robusta para seleção de arquivos e captura de fotos tanto dentro de WebViews quanto em apps nativos.
📋 Funcionalidades
- ✅ Abrir chooser nativo do Android
- ✅ Captura de fotos via câmera
- ✅ Seleção de arquivos de qualquer tipo
- ✅ Integração com WebViews (resolve problemas Android 14/15)
- ✅ Retorno de arquivos em base64 (opcional)
- ✅ Suporte multiplataforma (Android/Web)
- ✅ Gestão automática de permissões
📦 Instalação
npm install @kaymasoft/file-chooser
npx cap sync android⚙️ Configuração
Android
- Permissões necessárias - Adicione ao
android/app/src/main/AndroidManifest.xml:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />- FileProvider - Certifique-se que está configurado no
AndroidManifest.xml:
<application>
<!-- Outros componentes -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
</application>- file_paths.xml - Crie o arquivo
android/app/src/main/res/xml/file_paths.xml:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path name="images" path="images" />
<external-files-path name="external_files" path="." />
<files-path name="files" path="." />
</paths>🛠️ Uso Básico
Importação
import FileChooser from '@kaymasoft/file-chooser';Exemplos de Uso
1. Abrir Chooser Completo (Arquivo + Câmera)
async function abrirFileChooser() {
try {
const result = await FileChooser.open({
includeBase64: true // Opcional: inclui base64 no resultado
});
console.log('URI do arquivo:', result.uri);
console.log('Origem:', result.source); // 'file', 'camera' ou 'unknown'
console.log('Caminho:', result.path);
if (result.base64) {
console.log('Base64 disponível para WebView');
}
} catch (error) {
console.error('Erro ao abrir chooser:', error);
}
}2. Somente Seleção de Arquivos
async function selecionarArquivo() {
try {
const result = await FileChooser.openFileOnly();
console.log('Arquivo selecionado:', result.uri);
} catch (error) {
console.error('Seleção cancelada:', error);
}
}3. Somente Captura de Foto
async function capturarFoto() {
try {
const result = await FileChooser.openCameraOnly({
includeBase64: true
});
console.log('Foto capturada:', result.uri);
console.log('Base64:', result.base64);
} catch (error) {
console.error('Captura cancelada:', error);
}
}🌐 Integração com WebView
Para corrigir inputs file em WebViews:
import { InAppBrowser } from '@awesome-cordova-plugins/in-app-browser/ngx';
import FileChooser from '@kaymasoft/file-chooser';
async function abrirWebViewComFileChooser(url: string) {
const browser = this.iab.create(url, '_blank', {
location: 'no',
toolbar: 'no'
});
browser.on('loadstop').subscribe(() => {
// Injetar script para interceptar file inputs
this.injectFileChooserScript(browser);
});
browser.on('message').subscribe(async (event: any) => {
if (event.data.message === 'openFileChooser') {
await this.handleFileChooserRequest(browser, event.data);
}
});
}
private injectFileChooserScript(browser: any) {
const script = `
document.addEventListener('click', function(e) {
if (e.target.type === 'file') {
e.preventDefault();
var inputId = e.target.id || 'file_input_' + Date.now();
e.target.id = inputId;
window.webkit.messageHandlers.capacitor.postMessage({
type: 'message',
data: { message: 'openFileChooser', inputId: inputId }
});
}
}, true);
`;
browser.executeScript({ code: script });
}
private async handleFileChooserRequest(browser: any, data: any) {
try {
const result = await FileChooser.open({ includeBase64: true });
const injectScript = `
var input = document.getElementById('${data.inputId}');
if (input && '${result.base64}') {
var base64 = '${result.base64}'.split(',')[1];
var byteCharacters = atob(base64);
var byteNumbers = new Array(byteCharacters.length);
for (var i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
var byteArray = new Uint8Array(byteNumbers);
var fileName = '${result.source}' === 'camera' ? 'photo.jpg' : 'file';
var file = new File([byteArray], fileName);
var dt = new DataTransfer();
dt.items.add(file);
input.files = dt.files;
input.dispatchEvent(new Event('change', { bubbles: true }));
}
`;
browser.executeScript({ code: injectScript });
} catch (error) {
console.error('Erro no FileChooser:', error);
}
}📱 Exemplo Completo em Ionic
import { Component } from '@angular/core';
import FileChooser from '@kaymasoft/file-chooser';
@Component({
selector: 'app-file-upload',
template: `
<ion-content>
<ion-button (click)="abrirChooser()">
Selecionar Arquivo ou Tirar Foto
</ion-button>
<ion-button (click)="somenteArquivo()">
Somente Arquivo
</ion-button>
<ion-button (click)="somenteFoto()">
Somente Foto
</ion-button>
<div *ngIf="arquivoSelecionado">
<p>Arquivo: {{ arquivoSelecionado.uri }}</p>
<p>Fonte: {{ arquivoSelecionado.source }}</p>
</div>
</ion-content>
`
})
export class FileUploadPage {
arquivoSelecionado: any = null;
async abrirChooser() {
try {
this.arquivoSelecionado = await FileChooser.open({
includeBase64: true
});
} catch (error) {
console.error('Erro:', error);
}
}
async somenteArquivo() {
try {
this.arquivoSelecionado = await FileChooser.openFileOnly();
} catch (error) {
console.error('Erro:', error);
}
}
async somenteFoto() {
try {
this.arquivoSelecionado = await FileChooser.openCameraOnly({
includeBase64: true
});
} catch (error) {
console.error('Erro:', error);
}
}
}🔧 API Reference
Métodos
| Método | Descrição | Parâmetros |
|--------|-----------|------------|
| open() | Abre chooser com opções de arquivo e câmera | options?: { includeBase64?: boolean } |
| openFileOnly() | Abre somente seletor de arquivos | options?: { includeBase64?: boolean } |
| openCameraOnly() | Abre somente a câmera | options?: { includeBase64?: boolean } |
Resultado (FileChooserResult)
interface FileChooserResult {
uri: string; // URI do arquivo/foto
path?: string; // Caminho do arquivo
source: 'file' | 'camera' | 'unknown'; // Origem do arquivo
base64?: string; // Base64 (se includeBase64: true)
}🐛 Solução de Problemas
Erro de Compilação Java
Se receber erro sobre versão Java, certifique-se que o build.gradle do plugin usa a mesma versão Java do seu app:
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
}Permissões não Funcionam
Execute os comandos:
npx cap clean android
npx cap sync androidWebView não Intercepta File Inputs
Certifique-se de injetar o script após o loadstop e usar o postMessage correto para sua versão do Capacitor.
📄 Licença
MIT License - veja LICENSE para detalhes.
👨💻 Autor
Enio K S Meyer - Kaymasoft
🤝 Contribuições
Contribuições são bem-vindas! Por favor, abra uma issue ou pull request.
