range_request 0.2.0
range_request: ^0.2.0 copied to clipboard
A Dart package for efficient HTTP range requests supporting parallel chunked downloads, resume capability, and checksum verification.
Range Request #
A high-performance Dart package for HTTP range requests, enabling efficient file downloads with support for parallel chunked downloads, automatic resume capability, and checksum verification.
Features #
- ๐ Parallel chunked downloads - Split large files into chunks for concurrent downloading
- ๐ Automatic resume - Continue interrupted downloads from where they left off
- โ Checksum verification - Verify file integrity with MD5/SHA256
- ๐ฏ Smart fallback - Automatically falls back to serial download if server doesn't support range requests
- ๐ Progress tracking - Real-time download progress with customizable intervals
- โ Cancellation support - Gracefully cancel downloads with CancelToken
- ๐ง Highly configurable - Fine-tune chunk size, concurrency, retries, and more
- ๐๏ธ Isolate-based I/O - Non-blocking file operations using Dart isolates
- ๐งน Temporary file cleanup - Manual cleanup utility for incomplete downloads
Limitations and Advantages #
Limitations #
- No background downloads on mobile: Since this package doesn't use native device features, downloads cannot continue when the app is in the background on mobile platforms. Downloads will pause when the app is backgrounded and can be resumed when the app returns to the foreground.
Advantages #
- Cross-platform consistency: Pure Dart implementation ensures identical behavior across iOS, Android, Web, and Desktop platforms with high-performance parallel downloads when servers support range requests
- Minimal footprint: Depends only on
http
andcrypto
packages, keeping your app size small and reducing dependency vulnerabilities - Zero configuration: No native setup, platform-specific code, or complex configuration required - works out of the box on all platforms including Flutter Web
- Maintenance-friendly: No platform-specific bugs or OS update compatibility issues to worry about - one codebase for all platforms
Installation #
dart pub add range_request
Or add it manually to your pubspec.yaml
:
dependencies:
range_request:
Then run:
dart pub get
Usage #
Basic Streaming Download #
import 'package:range_request/range_request.dart';
void main() async {
final client = RangeRequestClient();
final url = Uri.parse('https://example.com/large-file.zip');
// Stream download with automatic chunking if supported
await for (final chunk in client.fetch(url)) {
// Process each chunk (e.g., write to file, update UI, etc.)
print('Received ${chunk.length} bytes');
}
}
File Download with Progress Tracking #
import 'package:range_request/range_request.dart';
void main() async {
final downloader = FileDownloader.fromConfig(
RangeRequestConfig(
chunkSize: 5 * 1024 * 1024, // 5MB chunks
maxConcurrentRequests: 4,
),
);
final result = await downloader.downloadToFile(
Uri.parse('https://example.com/video.mp4'),
'/downloads',
outputFileName: 'my_video.mp4',
onProgress: (bytes, total, status) {
final progress = (bytes / total * 100).toStringAsFixed(1);
print('Progress: $progress% - Status: $status');
},
);
print('Downloaded to: ${result.filePath}');
print('File size: ${result.fileSize} bytes');
}
Download with Resume Support #
// Downloads will automatically resume if interrupted
final result = await downloader.downloadToFile(
Uri.parse('https://example.com/large-file.iso'),
'/downloads',
resume: true, // Enable resume (default: true)
onProgress: (bytes, total, status) {
if (bytes > 0) {
print('Resuming from ${bytes} bytes...');
}
},
);
Note: While resume support can continue interrupted downloads, it cannot detect file corruption that may occur during write operations (e.g., if the application crashes while writing buffered data to disk). In such cases, the resumed download may result in a corrupted file. Consider using checksum verification to ensure file integrity after completion.
Checksum Verification #
final result = await downloader.downloadToFile(
Uri.parse('https://example.com/software.exe'),
'/downloads',
checksumType: ChecksumType.sha256,
onProgress: (bytes, total, status) {
if (status == DownloadStatus.calculatingChecksum) {
print('Verifying file integrity...');
}
},
);
print('SHA256: ${result.checksum}');
Using Cancellation Tokens #
final cancelToken = CancelToken();
// Start download in a separate async operation
final downloadFuture = downloader.downloadToFile(
Uri.parse('https://example.com/huge-file.bin'),
'/downloads',
cancelToken: cancelToken,
);
// Cancel the download after 5 seconds
Future.delayed(Duration(seconds: 5), () {
cancelToken.cancel();
print('Download cancelled');
});
try {
await downloadFuture;
} on RangeRequestException catch (e) {
if (e.code == RangeRequestErrorCode.cancelled) {
print('Download was cancelled');
}
}
Managing Multiple Downloads #
You can manage multiple downloads through the FileDownloader
's client instance:
final downloader = FileDownloader();
// Start multiple downloads
final download1 = downloader.downloadToFile(url1, '/downloads');
final download2 = downloader.downloadToFile(url2, '/downloads');
final download3 = downloader.downloadToFile(url3, '/downloads');
// Cancel all active downloads through the downloader's client
downloader.client.cancelAll();
// Or cancel and clear all tokens for a fresh start
downloader.client.cancelAndClear();
// Individual downloads can still use their own CancelToken
final token = CancelToken();
final download4 = downloader.downloadToFile(
url4,
'/downloads',
cancelToken: token,
);
token.cancel(); // Cancel individual download
Configuration Options #
final config = RangeRequestConfig(
chunkSize: 10 * 1024 * 1024, // 10MB chunks
maxConcurrentRequests: 8, // 8 parallel connections
maxRetries: 3, // Retry failed chunks 3 times
retryDelayMs: 1000, // Wait 1 second before retry
connectionTimeout: Duration(seconds: 30),
tempFileExtension: '.tmp', // Extension for partial downloads
headers: { // Custom headers
'Authorization': 'Bearer token',
},
);
final client = RangeRequestClient(config: config);
File Conflict Handling #
// Choose how to handle existing files
final result = await downloader.downloadToFile(
url,
'/downloads',
conflictStrategy: FileConflictStrategy.rename, // Creates "file(1).ext" if exists
// or FileConflictStrategy.overwrite (default)
// or FileConflictStrategy.error (throws exception)
);
Cleanup Temporary Files #
// Remove incomplete downloads older than 1 day
final deletedCount = await downloader.cleanupTempFiles(
'/downloads',
olderThan: Duration(days: 1),
);
print('Cleaned up $deletedCount temporary files');
API Reference #
Core Classes #
-
RangeRequestClient
: Main client for performing HTTP range requestsfetch()
: Stream download with optional progress callbackcheckServerInfo()
: Check if server supports range requestscancelAll()
: Cancel all active operationsclearTokens()
: Clear all tokens without cancellingcancelAndClear()
: Cancel all operations and clear tokens
-
FileDownloader
: High-level file download operationsdownloadToFile()
: Download directly to file with resume supportcleanupTempFiles()
: Clean up temporary download files
-
RangeRequestConfig
: Configuration for download behavior- Chunk size, concurrency, retries, timeouts, and more
-
CancelToken
: Token for cancelling download operationscancel()
: Cancel the associated downloadisCancelled
: Check if operation was cancelled
Enums #
ChecksumType
: Algorithm for file verification (sha256
,md5
,none
)DownloadStatus
: Current operation status (downloading
,calculatingChecksum
)FileConflictStrategy
: How to handle existing files (overwrite
,rename
,error
)
Advanced Features #
Parallel Chunked Downloads #
When the server supports range requests, files are automatically split into chunks and downloaded in parallel:
File: [====================] 100MB
โ
Chunks: [====][====][====][====][====] 5 x 20MB
โ โ โ โ โ
Parallel Downloads (up to maxConcurrentRequests)
โ โ โ โ โ
Reassembled: [====================]
Retry Logic #
Failed chunks are automatically retried with exponential backoff:
- First retry: 2 seconds delay (2x initial delay)
- Second retry: 4 seconds delay (4x initial delay)
- Third retry: 8 seconds delay (8x initial delay)
Memory Efficiency #
- Streaming API prevents loading entire files into memory
- Configurable buffer sizes for optimal performance
- Isolate-based checksum calculation prevents UI blocking
Error Handling #
try {
await downloader.downloadToFile(url, '/downloads');
} on RangeRequestException catch (e) {
switch (e.code) {
case RangeRequestErrorCode.networkError:
print('Network connection failed');
case RangeRequestErrorCode.serverError:
print('Server returned an error');
case RangeRequestErrorCode.fileError:
print('File system error occurred');
case RangeRequestErrorCode.checksumMismatch:
print('Downloaded file is corrupted');
case RangeRequestErrorCode.cancelled:
print('Download was cancelled');
case RangeRequestErrorCode.invalidResponse:
print('Invalid response from server');
case RangeRequestErrorCode.unsupportedOperation:
print('Operation not supported');
}
}