https://github.com/nikaera/aero_cache
A high-performance HTTP caching library for Dart/Flutter with zstd compression, ETag/Last-Modified revalidation, and full Cache-Control directive support. ☁️
https://github.com/nikaera/aero_cache
caching compression dart flutter http image media video
Last synced: 9 months ago
JSON representation
A high-performance HTTP caching library for Dart/Flutter with zstd compression, ETag/Last-Modified revalidation, and full Cache-Control directive support. ☁️
- Host: GitHub
- URL: https://github.com/nikaera/aero_cache
- Owner: nikaera
- License: mit
- Created: 2025-07-09T08:19:05.000Z (9 months ago)
- Default Branch: main
- Last Pushed: 2025-07-11T07:16:14.000Z (9 months ago)
- Last Synced: 2025-07-11T07:17:13.379Z (9 months ago)
- Topics: caching, compression, dart, flutter, http, image, media, video
- Language: Dart
- Homepage: https://pub.dev/packages/aero_cache
- Size: 147 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# AeroCache ☁️

[](https://github.com/nikaera/aero_cache/actions/workflows/project-ci.yaml)
[](https://pub.dev/packages/aero_cache)
[](https://pub.dev/packages/very_good_analysis)
A high-performance HTTP caching library for Dart/Flutter with zstd compression, ETag/Last-Modified revalidation, and full Cache-Control directive support.
## Features ✨🚀
- ⚡️ **High Performance**: Efficient caching with zstd compression for optimal storage
- 🏷️ **ETag Support**: Automatic cache revalidation using ETag headers
- 🕒 **Last-Modified Support**: Fallback cache validation using Last-Modified headers
- 🧩 **Vary Header Support**: Intelligent cache key generation based on Vary header specifications
- 🛡️ **Cache Control Directives**: Support for no-cache, no-store, must-revalidate, max-age, max-stale, min-fresh, only-if-cached, stale-while-revalidate, and stale-if-error
- 🔄 **Background Revalidation**: Stale-while-revalidate support for serving stale content while updating cache
- 🛠️ **Error Resilience**: Stale-if-error support for serving cached content during network failures
- 📈 **Progress Tracking**: Real-time download progress callbacks
- 🧹 **Automatic Cleanup**: Built-in expired cache cleanup
- ⚙️ **Flexible Configuration**: Customizable cache directory and compression settings
- 🚨 **Exception Handling**: Comprehensive error handling with custom exceptions
## Installation 🛠️
Add this to your package's `pubspec.yaml` file:
```yaml
dependencies:
aero_cache: ^1.0.0
```
Then run:
```bash
flutter pub get
```
## Usage 📦
### Basic Usage 🏁
```dart
import 'package:aero_cache/aero_cache.dart';
void main() async {
// Create AeroCache instance
final cache = AeroCache();
// Initialize the cache
await cache.initialize();
// Get data (downloads if not cached or stale)
final data = await cache.get('https://example.com/image.jpg');
// Use the data
print('Downloaded ${data.length} bytes');
// Clean up
cache.dispose();
}
```
### Advanced Usage ⚙️
```dart
import 'package:aero_cache/aero_cache.dart';
void main() async {
// Create AeroCache with custom configuration
final cache = AeroCache(
disableCompression: false, // Enable zstd compression
compressionLevel: 6, // Custom compression level (1-22)
cacheDirPath: '/custom/cache/path', // Custom cache directory
defaultCacheDuration: const Duration(hours: 24), // Custom cache duration
);
await cache.initialize();
// Get data with progress tracking
final data = await cache.get(
'https://example.com/large-file.zip',
onProgress: (received, total) {
final progress = (received / total * 100).toStringAsFixed(1);
print('Download progress: $progress%');
},
);
// Get metadata information
final metaInfo = await cache.metaInfo('https://example.com/large-file.zip');
if (metaInfo != null) {
print('ETag: ${metaInfo.etag}');
print('Last Modified: ${metaInfo.lastModified}');
print('Is Stale: ${metaInfo.isStale}');
print('Expires At: ${metaInfo.expiresAt}');
print('Content Type: ${metaInfo.contentType}');
}
// Clear expired cache
await cache.clearExpiredCache();
// Clear all cache
await cache.clearAllCache();
cache.dispose();
}
```
### Vary Header Handling 🧩
```dart
import 'package:aero_cache/aero_cache.dart';
void main() async {
final cache = AeroCache();
await cache.initialize();
// First request with English accept-language
final data1 = await cache.get(
'https://api.example.com/content',
headers: {
'Accept-Language': 'en-US',
'User-Agent': 'MyApp/1.0',
'Accept-Encoding': 'gzip',
},
);
// Second request with different accept-language
// This will create a separate cache entry if the server's response
// includes "Vary: Accept-Language"
final data2 = await cache.get(
'https://api.example.com/content',
headers: {
'Accept-Language': 'ja-JP',
'User-Agent': 'MyApp/1.0',
'Accept-Encoding': 'gzip',
},
);
cache.dispose();
}
```
### Cache Control Directives 🛡️
```dart
import 'package:aero_cache/aero_cache.dart';
void main() async {
final cache = AeroCache();
await cache.initialize();
// Force bypass cache and download fresh data
final freshData = await cache.get(
'https://api.example.com/data',
noCache: true,
);
// Only use cached data, fail if not available
try {
final cachedData = await cache.get(
'https://api.example.com/data',
onlyIfCached: true,
);
} catch (e) {
print('No cached data available');
}
// Accept stale data up to 3600 seconds old
final staleData = await cache.get(
'https://api.example.com/data',
maxStale: 3600,
);
// Require data to be fresh for at least 300 seconds
final freshRequiredData = await cache.get(
'https://api.example.com/data',
minFresh: 300,
);
// Download without caching (no-store equivalent)
final temporaryData = await cache.get(
'https://api.example.com/data',
noStore: true,
);
cache.dispose();
}
```
### Flutter Integration 🐦
```dart
import 'package:flutter/material.dart';
import 'package:aero_cache/aero_cache.dart';
class ImageWidget extends StatefulWidget {
final String imageUrl;
const ImageWidget({Key? key, required this.imageUrl}) : super(key: key);
@override
_ImageWidgetState createState() => _ImageWidgetState();
}
class _ImageWidgetState extends State {
final AeroCache _cache = AeroCache();
Uint8List? _imageData;
bool _isLoading = true;
double _progress = 0.0;
@override
void initState() {
super.initState();
_loadImage();
}
Future _loadImage() async {
try {
await _cache.initialize();
final data = await _cache.get(
widget.imageUrl,
onProgress: (received, total) {
setState(() {
_progress = received / total;
});
},
);
setState(() {
_imageData = data;
_isLoading = false;
});
} catch (e) {
setState(() {
_isLoading = false;
});
// Handle error
}
}
@override
Widget build(BuildContext context) {
if (_isLoading) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
const CircularProgressIndicator(),
const SizedBox(height: 8),
Text('${(_progress * 100).toInt()}%'),
],
);
}
if (_imageData != null) {
return Image.memory(_imageData!);
}
return const Icon(Icons.error);
}
@override
void dispose() {
_cache.dispose();
super.dispose();
}
}
```
[API Reference 📚](https://nikaera.com/aero_cache/)
## Performance ⚡️
AeroCache uses zstd compression by default, which provides:
- Fast compression/decompression speeds
- Excellent compression ratios
- Low memory usage
Benchmarks show significant storage savings compared to uncompressed caching, especially for text-based content like JSON and HTML.
## Vary Header Support 🧩
AeroCache intelligently handles the `Vary` header to ensure correct cache behavior when responses depend on request headers. When a server includes a `Vary` header in its response, AeroCache automatically:
- Parses the `Vary` header to identify which request headers affect the response
- Incorporates relevant request header values into the cache key calculation
- Ensures cache hits only occur when the specified request headers match exactly
- Supports common headers like `Accept-Encoding`, `User-Agent`, `Accept-Language`, etc.
This ensures that cached responses are served only when appropriate, preventing issues like serving compressed content to clients that don't support compression.
## Contributing 🤝
We welcome contributions to AeroCache! Please follow the GitHub Flow process:
### How to Contribute
1. **Fork the repository** on GitHub
2. **Create a feature branch** from `main`:
```bash
git checkout -b feature/your-feature-name
```
3. **Make your changes** and add tests if applicable
4. **Ensure all tests pass**:
```bash
flutter test
```
5. **Follow the code style** using `very_good_analysis`:
```bash
dart analyze
```
6. **Commit your changes** with a clear message:
```bash
git commit -m "Add: your feature description"
```
7. **Push to your fork**:
```bash
git push origin feature/your-feature-name
```
8. **Create a Pull Request** on GitHub with:
- Clear description of changes
- Reference any related issues
- Screenshots if applicable
### Development Setup 🧑💻
1. Clone the repository:
```bash
git clone https://github.com/your-username/aero_cache.git
cd aero_cache
```
2. Install dependencies:
```bash
flutter pub get
```
3. Run tests:
```bash
flutter test
```
4. Run the example:
```bash
cd example
flutter run
```
### Code Style 🎨
- Follow the official [Dart style guide](https://dart.dev/guides/language/effective-dart/style)
- Use `very_good_analysis` linting rules
- Write comprehensive tests for new features
- Add documentation for public APIs
## Support 💬
For questions and support:
- Check the [example](example/) directory for usage examples
- Open an issue on GitHub for bugs or feature requests
- Review the API documentation
### Reporting Issues 🐞
When reporting issues, please include:
- Flutter/Dart version
- Operating system
- Steps to reproduce
- Expected vs actual behavior
- Code samples if applicable
## Changelog 📝
See [CHANGELOG.md](CHANGELOG.md) for version history and changes.
## License 📄
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.