File Management API (Android)
The File Management modules provide native Android functionality for downloading files to public storage and opening folders in file managers. These features ensure exported files are immediately visible and accessible to users.
Android Only - These modules are currently available on Android. iOS support is planned for future releases.
FileDownloader Module
Download files to public storage with automatic MediaStore API integration for Android 10+ and legacy support for older versions.
Import
import { NativeModules } from 'react-native';
const { FileDownloader } = NativeModules;
Check Availability
if (FileDownloader) {
// Module is available
} else {
console.log('FileDownloader not available on this platform');
}
downloadToPublicFolder(sourcePath, fileName, mimeType)
Download a file from app's internal/cache storage to public Downloads folder.
const publicPath = await FileDownloader.downloadToPublicFolder(
'/data/user/0/com.app/cache/temp.pdf',
'document.pdf',
'application/pdf'
);
console.log('Downloaded to:', publicPath);
// Output: /storage/emulated/0/Download/PDFDemoApp/document.pdf
Parameters:
sourcePath(string): Full path to source file in app's cache or internal storagefileName(string): Desired file name for the downloaded filemimeType(string): MIME type of the file'application/pdf'- PDF documents'image/png'- PNG images'image/jpeg'- JPEG images
Returns: Promise<string>
- Full path to the downloaded file in public storage
Platform Behavior:
- Android 10+ (API 29+): Uses MediaStore API (Scoped Storage compliant)
- Android 9 and below: Uses legacy
Environment.getExternalStoragePublicDirectory()
Features:
- ✅ Automatic folder creation (
Downloads/PDFDemoApp) - ✅ Files immediately visible in file managers
- ✅ Smart notifications with "Open Folder" action
- ✅ Media scanner notification for legacy devices
- ✅ No WRITE_EXTERNAL_STORAGE permission needed on Android 10+
Complete Example
import React, { useState } from 'react';
import { View, Button, Alert, NativeModules } from 'react-native';
import { PDFExporter } from 'NativeModules';
const { FileDownloader } = NativeModules;
const ExportAndDownload = ({ pdfPath }) => {
const [downloading, setDownloading] = useState(false);
const exportAndDownload = async () => {
if (!FileDownloader) {
Alert.alert('Error', 'File download not available on this platform');
return;
}
setDownloading(true);
try {
// Step 1: Export page to image (to cache)
const cachedImagePath = await PDFExporter.exportPageToImage(
pdfPath,
0, // First page
{
format: 'png',
quality: 0.9,
scale: 2.0
}
);
// Step 2: Download to public storage
const publicPath = await FileDownloader.downloadToPublicFolder(
cachedImagePath,
'exported-page.png',
'image/png'
);
Alert.alert(
'✅ Success',
`File saved to:\n${publicPath}`,
[{ text: 'OK' }]
);
} catch (error) {
console.error('Download failed:', error);
Alert.alert('Error', error.message);
} finally {
setDownloading(false);
}
};
return (
<View>
<Button
title={downloading ? 'Downloading...' : 'Export & Download'}
onPress={exportAndDownload}
disabled={downloading}
/>
</View>
);
};
export default ExportAndDownload;
Error Handling
try {
const publicPath = await FileDownloader.downloadToPublicFolder(
sourcePath,
fileName,
mimeType
);
console.log('Success:', publicPath);
} catch (error) {
if (error.code === 'FILE_NOT_FOUND') {
console.error('Source file does not exist');
} else if (error.code === 'DOWNLOAD_ERROR') {
console.error('Failed to download:', error.message);
} else {
console.error('Unknown error:', error);
}
}
FileManager Module
Open folders in file managers with multiple fallback strategies for maximum compatibility.
Import
import { NativeModules } from 'react-native';
const { FileManager } = NativeModules;
openDownloadsFolder()
Open the Downloads/PDFDemoApp folder in the device's file manager.
try {
await FileManager.openDownloadsFolder();
console.log('Folder opened successfully');
} catch (error) {
console.error('Could not open folder:', error);
}
Parameters: None
Returns: Promise<boolean>
trueif folder was opened successfully
Throws: Error if all strategies fail
Fallback Strategies:
- Opens specific
Downloads/PDFDemoAppfolder via DocumentsUI - Opens system Downloads app
- Opens generic Files app
- Shows file picker for user to choose file manager
Complete Example with FileDownloader
import React, { useState } from 'react';
import { View, Button, Alert, NativeModules } from 'react-native';
const { FileDownloader, FileManager } = NativeModules;
const ExportWithFolderOpen = ({ pdfPath }) => {
const [exporting, setExporting] = useState(false);
const exportAndOpenFolder = async () => {
setExporting(true);
try {
// Export page to cache
const cachedPath = await PDFExporter.exportPageToImage(
pdfPath,
0,
{ format: 'png', quality: 0.9, scale: 2.0 }
);
// Download to public storage
const publicPath = await FileDownloader.downloadToPublicFolder(
cachedPath,
'page-1.png',
'image/png'
);
// Show success and offer to open folder
Alert.alert(
'✅ Export Complete',
'File saved to Downloads/PDFDemoApp',
[
{ text: 'Done', style: 'cancel' },
{
text: 'Open Folder',
onPress: async () => {
try {
await FileManager.openDownloadsFolder();
} catch (e) {
Alert.alert('Info', 'Please check Downloads/PDFDemoApp folder manually');
}
}
}
]
);
} catch (error) {
Alert.alert('Error', error.message);
} finally {
setExporting(false);
}
};
return (
<View>
<Button
title={exporting ? 'Exporting...' : 'Export & Open Folder'}
onPress={exportAndOpenFolder}
disabled={exporting}
/>
</View>
);
};
export default ExportWithFolderOpen;
Graceful Fallback
const tryOpenFolder = async () => {
if (!FileManager) {
// Not on Android or module not available
Alert.alert('Info', 'Please check Downloads/PDFDemoApp folder in your file manager');
return;
}
try {
await FileManager.openDownloadsFolder();
} catch (error) {
// All strategies failed
Alert.alert(
'Cannot Open Folder',
'Please check Downloads/PDFDemoApp folder manually in your file manager',
[{ text: 'OK' }]
);
}
};
Batch Download Example
Download multiple files and open folder at the end:
const batchExportAndDownload = async (pdfPath, pageNumbers) => {
const downloadedFiles = [];
try {
// Export all pages
for (let pageNum of pageNumbers) {
// Export to cache
const cachedPath = await PDFExporter.exportPageToImage(
pdfPath,
pageNum - 1,
{ format: 'png', quality: 0.9, scale: 2.0 }
);
// Download to public storage
const publicPath = await FileDownloader.downloadToPublicFolder(
cachedPath,
`page-${pageNum}.png`,
'image/png'
);
downloadedFiles.push(publicPath);
}
// Show success
Alert.alert(
'✅ Export Complete',
`${downloadedFiles.length} files saved to Downloads/PDFDemoApp`,
[
{ text: 'Done', style: 'cancel' },
{
text: 'Open Folder',
onPress: () => FileManager.openDownloadsFolder()
}
]
);
return downloadedFiles;
} catch (error) {
console.error('Batch download failed:', error);
throw error;
}
};
// Usage
batchExportAndDownload('/path/to/file.pdf', [1, 2, 3]);
Android Permissions
Android 10+ (API 29+)
No permissions required! MediaStore API doesn't need WRITE_EXTERNAL_STORAGE for adding files to public Downloads folder.
Android 9 and Below
Add to android/app/src/main/AndroidManifest.xml:
<uses-permission
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="28"
/>
This permission is automatically ignored on Android 10+ thanks to maxSdkVersion="28".
Customization
You can customize the folder name by modifying the native modules:
// In android/src/main/java/org/wonday/pdf/FileDownloader.java
private static final String FOLDER_NAME = "YourAppName";
// In android/src/main/java/org/wonday/pdf/FileManager.java
private static final String FOLDER_NAME = "YourAppName";
After modification, rebuild your app for Android.
Platform-Specific Code
Use Platform module to handle Android-only features:
import { Platform, NativeModules, Alert } from 'react-native';
const downloadFile = async (sourcePath, fileName, mimeType) => {
if (Platform.OS !== 'android') {
Alert.alert('Not Supported', 'File download is only available on Android');
return null;
}
const { FileDownloader } = NativeModules;
if (!FileDownloader) {
Alert.alert('Error', 'FileDownloader module not available');
return null;
}
try {
const publicPath = await FileDownloader.downloadToPublicFolder(
sourcePath,
fileName,
mimeType
);
return publicPath;
} catch (error) {
console.error('Download failed:', error);
return null;
}
};
TypeScript Support
interface FileDownloaderStatic {
downloadToPublicFolder(
sourcePath: string,
fileName: string,
mimeType: 'application/pdf' | 'image/png' | 'image/jpeg' | string
): Promise<string>;
}
interface FileManagerStatic {
openDownloadsFolder(): Promise<boolean>;
}
declare module 'react-native' {
interface NativeModulesStatic {
FileDownloader?: FileDownloaderStatic;
FileManager?: FileManagerStatic;
}
}
Best Practices
1. Always Check Availability
const { FileDownloader, FileManager } = NativeModules;
if (!FileDownloader || !FileManager) {
console.log('File management modules not available');
// Fallback to alternative solution
}
2. Handle Errors Gracefully
try {
await FileDownloader.downloadToPublicFolder(path, name, mime);
} catch (error) {
// Show user-friendly error message
Alert.alert('Download Failed', 'Could not save file. Please try again.');
}
3. Provide User Feedback
// Show loading indicator
setDownloading(true);
try {
const path = await FileDownloader.downloadToPublicFolder(...);
// Show success message
Alert.alert('Success', `File saved to ${path}`);
} finally {
setDownloading(false);
}
4. Clean Up Cache Files
import RNFS from 'react-native-fs';
// After downloading to public storage, optionally clean up cache
const cachedPath = await exportPage(...);
const publicPath = await FileDownloader.downloadToPublicFolder(...);
// Clean up cache file
await RNFS.unlink(cachedPath);
Troubleshooting
Files Not Appearing in File Manager
Solution: Files should appear immediately with MediaStore API. If not:
- Check that you're using the correct MIME type
- On Android 9 and below, the media scanner runs automatically
- Restart the file manager app
"Module Not Found" Error
Solution:
- Ensure you're running on Android
- Rebuild the app:
npm run android - Clear caches:
npm start --reset-cache
Permission Denied on Android 9
Solution: Add WRITE_EXTERNAL_STORAGE permission to AndroidManifest.xml
See Also
- Export API - Export PDFs to images
- PDFTextExtractor API - Extract text from PDFs
- PDF Component - Main PDF viewer