capacitor-file-downloader
v1.0.0
Published
Capacitor plugin for downloading large files with pause/resume support
Maintainers
Readme
Capacitor File Downloader
A Capacitor 7 plugin for downloading large files (1GB+) with progress tracking and offline playback capabilities for Android and iOS.
Features
- ✅ Download large files (1GB+) without memory issues
- ✅ Progress tracking with real-time events
- ✅ Pause/Resume support (iOS)
- ✅ Offline video playback support
- ✅ TypeScript support with full type definitions
- ✅ React hooks for easy integration
- ✅ WebView-safe URLs via
Capacitor.convertFileSrc()
Installation
npm install capacitor-file-downloader
npx cap syncPlatform Support
| Platform | Download | Pause | Resume | Progress | |----------|----------|-------|--------|----------| | Android | ✅ | ❌ | ❌ | ✅ | | iOS | ✅ | ✅ | ✅ | ✅ | | Web | ⚠️** | ❌ | ❌ | ❌ |
*Android uses an in-app OkHttp stream (no system notifications, no background continuation). Use cancel() to stop.
**Web implementation uses basic browser download (fallback only).
Android Configuration
Permissions
Minimal permissions are required. The plugin uses in-app networking (OkHttp) and writes to your app-scoped external files directory.
<uses-permission android:name="android.permission.INTERNET" />File Storage
Files are saved to the app's external files directory:
/storage/emulated/0/Android/data/[your.app.id]/files/[directory]/[fileName]iOS Configuration
Info.plist
No additional configuration required. Files are saved to the app's Documents directory:
[App Container]/Documents/[directory]/[fileName]Background Downloads
iOS supports background downloads via URLSession.
Usage
Basic Usage (TypeScript)
import { CapacitorFileDownloader } from 'capacitor-file-downloader';
// Start a download
const result = await CapacitorFileDownloader.download({
url: 'https://example.com/large-video.mp4',
fileName: 'video.mp4',
directory: 'videos' // optional, defaults to 'downloads'
});
console.log('Download ID:', result.id);
console.log('File path:', result.filePath);
// Listen for progress
CapacitorFileDownloader.addListener('progress', (progress) => {
console.log(`Download ${progress.id}: ${progress.progress}%`);
console.log(`${progress.bytesDownloaded} / ${progress.totalBytes} bytes`);
});
// Listen for completion
CapacitorFileDownloader.addListener('complete', (result) => {
console.log('Download complete:', result.filePath);
});
// Listen for errors
CapacitorFileDownloader.addListener('error', (error) => {
console.error('Download error:', error.error);
});
// Pause (iOS only)
await CapacitorFileDownloader.pause({ id: result.id });
// Resume (iOS only)
await CapacitorFileDownloader.resume({ id: result.id });
// Cancel
await CapacitorFileDownloader.cancel({ id: result.id });
// Get status
const status = await CapacitorFileDownloader.getStatus({ id: result.id });
console.log('Status:', status.status);
console.log('Progress:', status.progress);Using the Wrapper (Recommended)
import { fileDownloader } from 'capacitor-file-downloader/dist/esm/wrapper';
// Download with callbacks
const result = await fileDownloader.download(
{
url: 'https://example.com/video.mp4',
fileName: 'my-video.mp4',
directory: 'videos'
},
{
onProgress: (progress) => {
console.log(`Progress: ${progress.progress}%`);
},
onComplete: (result) => {
console.log('Complete:', result.filePath);
},
onError: (error) => {
console.error('Error:', error.error);
}
}
);
// Get WebView-safe URL for video playback
const webViewUrl = fileDownloader.getWebViewPath(result.filePath);
console.log('WebView URL:', webViewUrl);
// Download video optimized for offline playback
const videoResult = await fileDownloader.downloadVideo(
'https://example.com/video.mp4',
'my-video.mp4',
(progress) => {
console.log(`${progress.progress}%`);
}
);
// Use in video element
// <video src={videoResult.webViewUrl} controls />React Hook Usage
import { useFileDownloader } from 'capacitor-file-downloader/dist/esm/react-hook';
function DownloadComponent() {
const { download, downloads, cancel, pause, resume } = useFileDownloader();
const handleDownload = async () => {
const result = await download({
url: 'https://example.com/video.mp4',
fileName: 'video.mp4',
directory: 'videos'
});
console.log('Started download:', result.id);
};
return (
<div>
<button onClick={handleDownload}>Download</button>
{downloads.map(download => (
<div key={download.id}>
<p>Status: {download.status}</p>
<p>Progress: {download.progress}%</p>
<p>{download.bytesDownloaded} / {download.totalBytes} bytes</p>
{download.status === 'downloading' && (
<>
<button onClick={() => pause(download.id)}>Pause</button>
<button onClick={() => cancel(download.id)}>Cancel</button>
</>
)}
{download.status === 'paused' && (
<button onClick={() => resume(download.id)}>Resume</button>
)}
{download.status === 'completed' && download.webViewUrl && (
<video src={download.webViewUrl} controls />
)}
</div>
))}
</div>
);
}Single Download Hook
import { useDownload } from 'capacitor-file-downloader/dist/esm/react-hook';
function VideoDownloader() {
const {
progress,
status,
webViewUrl,
download,
reset
} = useDownload();
const handleDownload = async () => {
await download(
'https://example.com/video.mp4',
'video.mp4',
'videos'
);
};
return (
<div>
{status === 'pending' && (
<button onClick={handleDownload}>Download Video</button>
)}
{status === 'downloading' && (
<div>
<p>Downloading: {progress}%</p>
<progress value={progress} max={100} />
</div>
)}
{status === 'completed' && webViewUrl && (
<div>
<p>Download complete!</p>
<video src={webViewUrl} controls />
</div>
)}
{status === 'error' && (
<p>Error downloading file</p>
)}
</div>
);
}Ionic React Example
import { IonButton, IonProgressBar, IonCard } from '@ionic/react';
import { useFileDownloader } from 'capacitor-file-downloader/dist/esm/react-hook';
const VideoDownloadPage: React.FC = () => {
const { download, downloads, cancel } = useFileDownloader();
const downloadVideo = async () => {
await download({
url: 'https://example.com/large-video.mp4',
fileName: `video-${Date.now()}.mp4`,
directory: 'videos'
});
};
return (
<div>
<IonButton onClick={downloadVideo}>Download Video</IonButton>
{downloads.map(d => (
<IonCard key={d.id}>
<h3>{d.status}</h3>
<IonProgressBar value={d.progress / 100} />
<p>{d.progress.toFixed(1)}%</p>
{d.status === 'downloading' && (
<IonButton onClick={() => cancel(d.id)}>Cancel</IonButton>
)}
{d.status === 'completed' && d.webViewUrl && (
<video
src={d.webViewUrl}
controls
style={{ width: '100%' }}
/>
)}
</IonCard>
))}
</div>
);
};API Reference
download(options: DownloadOptions): Promise<DownloadResult>
Start downloading a file.
Options:
url: string- URL to download fromfileName: string- Name to save the file asdirectory?: string- Optional subdirectory (default: 'downloads')headers?: { [key: string]: string }- Optional HTTP headers
Returns:
id: string- Unique download identifierfilePath: string- Native file path
pause(options: { id: string }): Promise<void>
Pause a download (iOS only).
resume(options: { id: string }): Promise<void>
Resume a paused download (iOS only).
cancel(options: { id: string }): Promise<void>
Cancel an active download.
getStatus(options: { id: string }): Promise<DownloadStatus>
Get the current status of a download.
Returns:
id: stringstatus: 'pending' | 'downloading' | 'paused' | 'completed' | 'failed' | 'cancelled'progress: number- Progress percentage (0-100)filePath?: string- Available when completederror?: string- Error message if failed
Events
progress
Emitted during download progress.
{
id: string;
progress: number; // 0-100
bytesDownloaded: number;
totalBytes: number;
}complete
Emitted when download completes successfully.
{
id: string;
filePath: string;
}error
Emitted when download fails.
{
id: string;
error: string;
}Offline Video Playback
To use downloaded videos in a <video> element:
import { Capacitor } from '@capacitor/core';
import { CapacitorFileDownloader } from 'capacitor-file-downloader';
// Download video
const result = await CapacitorFileDownloader.download({
url: 'https://example.com/video.mp4',
fileName: 'offline-video.mp4',
directory: 'videos'
});
// Convert to WebView URL
const webViewUrl = Capacitor.convertFileSrc(result.filePath);
// Use in video element
<video src={webViewUrl} controls />Important: Always use Capacitor.convertFileSrc() to convert native file paths to WebView-safe URLs.
File Paths
Android
/storage/emulated/0/Android/data/[your.app.id]/files/[directory]/[fileName]iOS
file:///var/mobile/Containers/Data/Application/[UUID]/Documents/[directory]/[fileName]WebView URLs
Android:
http://localhost/_capacitor_file_/storage/emulated/0/Android/data/[your.app.id]/files/[directory]/[fileName]iOS:
capacitor://localhost/_capacitor_file_/var/mobile/Containers/Data/Application/[UUID]/Documents/[directory]/[fileName]Handling Large Files (1GB+)
This plugin is optimized for large files:
Android
- Uses OkHttp streaming directly to disk (no system UI)
- Handles multi-GB files by streaming; stays in-app (no background continuation)
iOS
- Uses
URLSessionDownloadTaskfor streaming downloads - Writes directly to disk without loading into memory
- Supports background downloads via
URLSession
Best Practices
- Always use WebView URLs - Use
Capacitor.convertFileSrc()for all file paths - Handle permissions - Request storage permissions on Android if needed
- Check platform - Pause/Resume only work on iOS
- Clean up listeners - Remove listeners when component unmounts
- Handle errors - Always listen for error events
- Use directories - Organize files in subdirectories (videos, documents, etc.)
Troubleshooting
Video not playing in WebView
Make sure to use Capacitor.convertFileSrc():
const webViewUrl = Capacitor.convertFileSrc(nativeFilePath);Android pause/resume
Android path does not support pause/resume. Use cancel() and restart the download if needed.
Download fails with permission error
On Android 13+, ensure notification permissions are granted. On older Android versions, check storage permissions.
iOS background download stops
iOS may suspend downloads in background if the app is terminated. Use URLSession background configuration (already implemented in the plugin).
License
MIT
Support
For issues, questions, or contributions, please visit the GitHub repository.
