npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

capacitor-file-downloader

v1.0.0

Published

Capacitor plugin for downloading large files with pause/resume support

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 sync

Platform 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 from
  • fileName: string - Name to save the file as
  • directory?: string - Optional subdirectory (default: 'downloads')
  • headers?: { [key: string]: string } - Optional HTTP headers

Returns:

  • id: string - Unique download identifier
  • filePath: 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: string
  • status: 'pending' | 'downloading' | 'paused' | 'completed' | 'failed' | 'cancelled'
  • progress: number - Progress percentage (0-100)
  • filePath?: string - Available when completed
  • error?: 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 URLSessionDownloadTask for streaming downloads
  • Writes directly to disk without loading into memory
  • Supports background downloads via URLSession

Best Practices

  1. Always use WebView URLs - Use Capacitor.convertFileSrc() for all file paths
  2. Handle permissions - Request storage permissions on Android if needed
  3. Check platform - Pause/Resume only work on iOS
  4. Clean up listeners - Remove listeners when component unmounts
  5. Handle errors - Always listen for error events
  6. 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.