A robust Flutter package for uploading large video and audio files with advanced features like chunked uploads, pause/resume functionality, network resilience, comprehensive progress tracking, and detailed logging. Built for reliability and user experience.
- Chunked Uploads: Automatically splits large video and audio files into manageable chunks ( configurable size)
- Resumable Uploads: Pause and resume uploads from where they left off
- Network Resilience: Automatic retry mechanism with configurable retry attempts and delays
- Chunk-Level Retry Tracking: Advanced retry system that tracks retries per individual chunk, preventing sluggishness and improving upload reliability
- Real-time Progress Tracking: Detailed progress updates with chunk-level information
- Network Monitoring: Automatic detection of network connectivity changes
- Error Handling: Comprehensive error handling with specific error codes and messages
- Advanced Logging: Configurable logging system with multiple log levels for debugging and monitoring
The SDK implements an advanced chunk-level retry tracking system that provides several benefits over traditional global retry counters:
- Prevents App Sluggishness: Each chunk maintains its own retry count, preventing one problematic chunk from affecting others
- Better Error Isolation: Failed chunks don't impact the retry limits of successful chunks
- Improved Reliability: Individual chunk retry tracking allows for more precise error handling
- Enhanced Monitoring: Detailed retry statistics per chunk for better debugging
// Each chunk maintains its own retry count
Map<int, int> chunkRetryCount = {}; // Track retries per chunk
// Check if a specific chunk should be retried
bool shouldRetryChunk(int chunkIndex) {
final retries = chunkRetryCount[chunkIndex] ?? 0;
return retries < maxRetries;
}
// Record a retry attempt for a specific chunk
void recordChunkRetry(int chunkIndex) {
chunkRetryCount[chunkIndex] = (chunkRetryCount[chunkIndex] ?? 0) + 1;
}
The SDK provides detailed logging of chunk retry statistics:
[SDK] DEBUG: Chunk Retry Statistics:
[SDK] DEBUG: Total Chunks: 16
[SDK] DEBUG: Max Retries: 5
[SDK] DEBUG: Retried Chunks: 2
[SDK] DEBUG: Chunk 3: 2/3 attempts (RETRYING)
[SDK] DEBUG: Chunk 7: 1/3 attempts (RETRYING)
To get started with SDK, you will need a signed URL. To make API requests, you'll need a valid Access Token and Secret Key. See the Basic Authentication Guide for details on retrieving these credentials.
Once you have your credentials, use the Upload media from device API to generate a signed URL for uploading media.
A signed URL is a pre-authenticated URL that allows secure, direct uploads to cloud storage services (like AWS S3, Google Cloud Storage, Azure Blob Storage, etc.) without exposing your storage credentials in your mobile app.
Here are examples of how to generate signed URLs using different backend services:
import 'dart:convert';
import 'package:http/http.dart' as http;
class SignedUrlService {
static const String TOKEN_ID = "your_token_id";
static const String SECRET_KEY = "your_secret_key";
static const String API_BASE_URL = "https://api.fastpix.app/v1/on-demand/upload";
Future<String> generateSignedUrl({
String? corsOrigin = "*",
Map<String, dynamic>? metadata,
String accessPolicy = "public",
String maxResolution = "1080p",
}) async {
try {
// Prepare authentication
final credentials = '$TOKEN_ID:$SECRET_KEY';
final auth = 'Basic ${base64.encode(utf8.encode(credentials))}';
// Prepare request body
final requestBody = {
"corsOrigin": corsOrigin,
"pushMediaSettings": {
"metadata": metadata ?? {"uploadedBy": "flutter_app"},
"accessPolicy": accessPolicy,
"maxResolution": maxResolution,
},
};
// Make API request
final response = await http.post(
Uri.parse(API_BASE_URL),
headers: {
'Authorization': auth,
'Content-Type': 'application/json',
},
body: jsonEncode(requestBody),
);
if (response.statusCode == 201) {
return response.body;
} else {
throw Exception('Failed to generate signed URL: ${response.reasonPhrase}');
}
} catch (e) {
throw Exception('Error generating signed URL: $e');
}
}
}
Here's how to integrate signed URL generation with the Flutter Resumable Uploads SDK:
import 'dart:io';
import 'package:fp_resumable_uploads/fp_resumable_uploads.dart';
class UploadService {
final SignedUrlService _signedUrlService = SignedUrlService();
Future<void> uploadVideo(File videoFile) async {
try {
// Step 1: Generate signed URL
final signedUrl = await _signedUrlService.generateSignedUrl(
metadata: {
'uploadedBy': 'flutter_app',
'fileType': 'video',
'timestamp': DateTime.now().toIso8601String(),
},
);
// Step 2: Configure and start upload
final uploadService = FlutterResumableUploads.builder()
.file(videoFile)
.signedUrl(signedUrl)
.chunkSize(16 * 1024 * 1024) // 16MB chunks
.maxRetries(3)
.enableLogging()
.onProgress((progress) {
print('Upload Progress: ${progress.uploadPercentage}%');
})
.onError((error) {
print('Upload Error: ${error.message}');
})
.build();
// Step 3: Start upload
await uploadService.uploadVideo();
} catch (e) {
print('Failed to upload video: $e');
}
}
}
import 'dart:io';
import 'package:fp_resumable_uploads/fp_resumable_uploads.dart';
void uploadVideo() async {
final uploadService = FlutterResumableUploads.builder()
.file(File('path/to/video.mp4'))
.signedUrl('your_signed_url_here')
.chunkSize(16 * 1024 * 1024) // 16MB chunks
.maxRetries(3)
.retryDelay(Duration(milliseconds: 2000))
.enableLogging() // Enable logging with default settings
.onProgress((progress) {
print('Upload Progress: ${progress.uploadPercentage}%');
print('Current Chunk: ${progress.currentChunkIndex}/${progress.totalChunks}');
print('Status: ${progress.status}');
})
.onError((error) {
print('Upload Error: ${error.message} (Code: ${error.code})');
})
.onPause(() {
print('Upload paused');
})
.onAbort(() {
print('Upload aborted');
})
.build();
await uploadService.uploadVideo();
}
import 'dart:io';
import 'package:fp_resumable_uploads/fp_resumable_uploads.dart';
void advancedUploadExample() async {
final uploadService = FlutterResumableUploads.builder()
.file(File('path/to/large_video.mp4'))
.signedUrl('your_signed_url_here')
.chunkSize(32 * 1024 * 1024) // 32MB chunks for faster upload
.maxFileSize(2 * 1024 * 1024 * 1024) // 2GB max file size
.maxRetries(5) // More retries for unstable connections
.retryDelay(Duration(seconds: 5)) // Longer delay between retries
.enableLoggingWithLevel(LogLevel.debug) // Enable debug logging
.logTag('[MyApp]') // Custom log tag
.onProgress((progress) {
// Custom progress handling
updateUI(progress);
})
.onError((error) {
// Custom error handling
handleUploadError(error);
})
.build();
// Start upload
await uploadService.uploadVideo();
// Control upload flow
if (uploadService.isPause()) {
uploadService.pauseUpload();
}
// Resume when ready
uploadService.resumeUpload();
// Abort if needed
uploadService.abortUpload();
// Check upload status
print('Upload Status: ${uploadService.currentStatus}');
print('Is Uploading: ${uploadService.isUploading()}');
print('Is Ready: ${uploadService.isReadyForUpload}');
// Clean up when done
uploadService.dispose();
}
The SDK includes a comprehensive logging system that helps developers debug upload issues and monitor upload performance.
The SDK supports multiple log levels to control the verbosity of logging:
Log Level | Description | Use Case |
---|---|---|
LogLevel.none |
No logging | Production builds |
LogLevel.error |
Only errors | Basic error tracking |
LogLevel.warning |
Errors and warnings | Error and warning tracking |
LogLevel.info |
Errors, warnings, and info | General monitoring |
LogLevel.debug |
All messages including debug | Development debugging |
LogLevel.verbose |
All messages including verbose | Detailed debugging |
final uploadService = FlutterResumableUploads.builder()
.file(File('path/to/video.mp4'))
.signedUrl('your_signed_url_here')
.enableLogging() // Enable with default INFO level
.build();
final uploadService = FlutterResumableUploads.builder()
.file(File('path/to/video.mp4'))
.signedUrl('your_signed_url_here')
.enableLoggingWithLevel(LogLevel.debug) // Enable with DEBUG level
.logTag('[VideoUpload]') // Custom log tag
.build();
// Configure logger globally
SDKLogger.setEnabled(true);
SDKLogger.setLogLevel(LogLevel.debug);
SDKLogger.setTag('[MyApp]');
// Then use the upload service normally
final uploadService = FlutterResumableUploads.builder()
.file(File('path/to/video.mp4'))
.signedUrl('your_signed_url_here'
)
.
build
(
);
The logging system provides detailed information about various aspects of the upload process:
- File path and size
- Chunk size and total chunks
- Retry settings
- Signed URL (truncated for security)
- Network connectivity changes
- Online/offline status transitions
- Individual chunk upload details
- Byte ranges and progress percentages
- Success/failure status for each chunk
- Detailed error messages with stack traces
- Retry attempts and delays
- HTTP status codes and error responses
- Pause/resume events
- Abort operations
- Upload completion
- Upload duration
- Average upload speed
- Memory usage (when available)
When logging is enabled, you'll see output like this:
[MyApp] INFO: Flutter Resumable Uploads SDK initialized
[MyApp] INFO: Log Level: LogLevel.debug
[MyApp] INFO: Debug Mode: true
[MyApp] INFO: Upload Configuration:
[MyApp] INFO: File: /path/to/video.mp4
[MyApp] INFO: File Size: 256.5 MB
[MyApp] INFO: Chunk Size: 16.0 MB
[MyApp] INFO: Total Chunks: 16
[MyApp] INFO: Max Retries: 3
[MyApp] INFO: Retry Delay: 2000ms
[MyApp] DEBUG: Chunk Upload: 1/16
[MyApp] DEBUG: Range: 0 - 16777216
[MyApp] DEBUG: Size: 16.0 MB
[MyApp] DEBUG: Progress: 0.0%
[MyApp] DEBUG: Chunk 1 uploaded successfully
[MyApp] INFO: Network Status: ONLINE
[MyApp] INFO: Upload Completed Successfully!
[MyApp] INFO: Total Chunks: 16
[MyApp] INFO: Total Bytes: 268.4 MB
[MyApp] INFO: Duration: 2m 15s
[MyApp] INFO: Average Speed: 2.0 MB/s
Method | Description |
---|---|
setEnabled(bool enabled) |
Enable or disable logging |
setLogLevel(LogLevel level) |
Set the log level |
setTag(String tag) |
Set custom log tag |
get logLevel |
Get current log level |
get isEnabled |
Check if logging is enabled |
Method | Description |
---|---|
error(String message, [Object? error, StackTrace? stackTrace]) |
Log error messages |
warning(String message) |
Log warning messages |
info(String message) |
Log info messages |
debug(String message) |
Log debug messages |
verbose(String message) |
Log verbose messages |
Method | Description |
---|---|
logUploadConfig(...) |
Log upload configuration details |
logChunkUpload(...) |
Log chunk upload details |
logNetworkStatus(bool isOnline) |
Log network status changes |
logUploadState(...) |
Log upload state changes |
logRetryAttempt(...) |
Log retry attempts |
logUploadCompletion(...) |
Log upload completion |
logUploadFailure(...) |
Log upload failures |
logPerformance(...) |
Log performance metrics |
logSDKInitialization() |
Log SDK initialization |
- Development: Use
LogLevel.debug
orLogLevel.verbose
for detailed debugging - Testing: Use
LogLevel.info
to monitor upload behavior - Production: Use
LogLevel.error
orLogLevel.none
to minimize overhead - Custom Tags: Use meaningful tags to identify different upload sessions
- Error Handling: Always check logs when uploads fail for detailed error information
The builder class provides a fluent API for configuring uploads:
Method | Description | Default |
---|---|---|
file(File file) |
Set the video file to upload | Required |
signedUrl(String url) |
Set the signed URL for upload | Required |
chunkSize(int bytes) |
Set chunk size in bytes | 16MB |
maxFileSize(int bytes) |
Set maximum allowed file size | No limit |
maxRetries(int count) |
Set maximum retry attempts | 3 |
retryDelay(Duration delay) |
Set delay between retries | 2 seconds |
Method | Description |
---|---|
enableLogging() |
Enable logging with default INFO level |
enableLoggingWithLevel(LogLevel level) |
Enable logging with custom level |
logTag(String tag) |
Set custom log tag |
Method | Description |
---|---|
onProgress(UploadProgressCallback) |
Progress update callback |
onError(ErrorCallback) |
Error handling callback |
onPause(PauseCallback) |
Pause event callback |
onAbort(AbortCallback) |
Abort event callback |
Method | Description |
---|---|
build() |
Create configured upload service |
buildAndUpload() |
Build and start upload immediately |
The main upload service class:
Method | Description |
---|---|
uploadVideo() |
Start upload using builder configuration |
uploadVideoWithParams(...) |
Start upload with direct parameters |
Method | Description |
---|---|
pauseUpload() |
Pause the current upload |
resumeUpload() |
Resume a paused upload |
abortUpload() |
Abort the current upload |
dispose() |
Clean up resources |
Method | Description |
---|---|
isUploading() |
Check if upload is in progress |
isPause() |
Check if upload is paused |
isReadyForUpload |
Check if ready to start upload |
currentStatus |
Get current upload status |
The progress model provides detailed upload information:
Property | Type | Description |
---|---|---|
status |
String | Current upload status |
uploadPercentage |
double | Upload completion percentage (0.0-100.0) |
currentChunkIndex |
int | Index of current chunk being uploaded |
totalChunks |
int | Total number of chunks |
chunksUploaded |
int | Number of successfully uploaded chunks |
isCompleted |
bool | Whether upload is completed |
Error model with detailed error information:
Property | Type | Description |
---|---|---|
code |
int | Error code (100-500) |
message |
String | Human-readable error message |
- Automatic Retry: Built-in retry mechanism handles network failures
- Network Monitoring: Detects connectivity changes and adapts accordingly
- Error Recovery: Comprehensive error handling with specific error codes
- State Persistence: Maintains upload state across app restarts
- Real-time Progress: Detailed progress updates with chunk-level information
- Pause/Resume: Users can pause and resume uploads at any time
- Background Uploads: Uploads continue even when app is in background
- Memory Efficient: Streams files without loading entire content into memory
- Builder Pattern: Clean, fluent API for easy configuration
- Type Safety: Full TypeScript support with proper type definitions
- Comprehensive Callbacks: Detailed progress and error callbacks
- Easy Integration: Simple setup with minimal configuration required
- Chunked Uploads: Efficient handling of large files
- Configurable Chunk Size: Optimize for different network conditions
- Streaming: Memory-efficient file processing
- Concurrent Uploads: Support for multiple simultaneous uploads
- iOS Support: Native iOS implementation
- Android Support: Native Android implementation
- Web Support: Web-compatible implementation
- Consistent API: Same API across all platforms
- β iOS 12.0+
- β Android API 21+
- β Flutter 1.17.0+
This project is licensed under the MIT License - see the LICENSE file for details.
If you encounter any issues or have questions:
- Check the Issues page
- Create a new issue with detailed information
- Include your Flutter version, platform, and error logs
See CHANGELOG.md for a complete list of changes and version history.