mirror of
https://github.com/venera-app/venera.git
synced 2025-09-27 07:47:24 +00:00
Improve image cache. Close #326
This commit is contained in:
@@ -13,7 +13,7 @@ export "widget_utils.dart";
|
||||
export "context.dart";
|
||||
|
||||
class _App {
|
||||
final version = "1.4.0";
|
||||
final version = "1.4.1";
|
||||
|
||||
bool get isAndroid => Platform.isAndroid;
|
||||
|
||||
|
@@ -21,7 +21,7 @@ class CacheManager {
|
||||
|
||||
int _limitSize = 2 * 1024 * 1024 * 1024;
|
||||
|
||||
CacheManager._create(){
|
||||
CacheManager._create() {
|
||||
Directory(cachePath).createSync(recursive: true);
|
||||
_db = sqlite3.open('${App.dataPath}/cache.db');
|
||||
_db.execute('''
|
||||
@@ -33,100 +33,102 @@ class CacheManager {
|
||||
type TEXT
|
||||
)
|
||||
''');
|
||||
compute((path) => Directory(path).size, cachePath)
|
||||
.then((value) => _currentSize = value);
|
||||
compute((path) => Directory(path).size, cachePath).then((value) {
|
||||
_currentSize = value;
|
||||
checkCache();
|
||||
});
|
||||
}
|
||||
|
||||
/// Get the singleton instance of CacheManager.
|
||||
factory CacheManager() => instance ??= CacheManager._create();
|
||||
|
||||
/// set cache size limit in MB
|
||||
void setLimitSize(int size){
|
||||
void setLimitSize(int size) {
|
||||
_limitSize = size * 1024 * 1024;
|
||||
}
|
||||
|
||||
void setType(String key, String? type){
|
||||
_db.execute('''
|
||||
UPDATE cache
|
||||
SET type = ?
|
||||
WHERE key = ?
|
||||
''', [type, key]);
|
||||
}
|
||||
|
||||
String? getType(String key){
|
||||
var res = _db.select('''
|
||||
SELECT type FROM cache
|
||||
WHERE key = ?
|
||||
''', [key]);
|
||||
if(res.isEmpty){
|
||||
return null;
|
||||
}
|
||||
return res.first[0];
|
||||
}
|
||||
|
||||
Future<void> writeCache(String key, List<int> data, [int duration = 7 * 24 * 60 * 60 * 1000]) async{
|
||||
/// Write cache to disk.
|
||||
Future<void> writeCache(String key, List<int> data,
|
||||
[int duration = 7 * 24 * 60 * 60 * 1000]) async {
|
||||
this.dir++;
|
||||
this.dir %= 100;
|
||||
var dir = this.dir;
|
||||
var name = md5.convert(Uint8List.fromList(key.codeUnits)).toString();
|
||||
var name = md5.convert(key.codeUnits).toString();
|
||||
var file = File('$cachePath/$dir/$name');
|
||||
while(await file.exists()){
|
||||
name = md5.convert(Uint8List.fromList(name.codeUnits)).toString();
|
||||
file = File('$cachePath/$dir/$name');
|
||||
}
|
||||
await file.create(recursive: true);
|
||||
await file.writeAsBytes(data);
|
||||
var expires = DateTime.now().millisecondsSinceEpoch + duration;
|
||||
_db.execute('''
|
||||
INSERT OR REPLACE INTO cache (key, dir, name, expires) VALUES (?, ?, ?, ?)
|
||||
''', [key, dir.toString(), name, expires]);
|
||||
if(_currentSize != null) {
|
||||
if (_currentSize != null) {
|
||||
_currentSize = _currentSize! + data.length;
|
||||
}
|
||||
checkCacheIfRequired();
|
||||
}
|
||||
|
||||
Future<CachingFile> openWrite(String key) async{
|
||||
this.dir++;
|
||||
this.dir %= 100;
|
||||
var dir = this.dir;
|
||||
var name = md5.convert(Uint8List.fromList(key.codeUnits)).toString();
|
||||
var file = File('$cachePath/$dir/$name');
|
||||
while(await file.exists()){
|
||||
name = md5.convert(Uint8List.fromList(name.codeUnits)).toString();
|
||||
file = File('$cachePath/$dir/$name');
|
||||
}
|
||||
await file.create(recursive: true);
|
||||
return CachingFile._(key, dir.toString(), name, file);
|
||||
}
|
||||
|
||||
Future<File?> findCache(String key) async{
|
||||
/// Find cache by key.
|
||||
/// If cache is expired, it will be deleted and return null.
|
||||
/// If cache is not found, it will return null.
|
||||
/// If cache is found, it will return the file, and update the expires time.
|
||||
Future<File?> findCache(String key) async {
|
||||
var res = _db.select('''
|
||||
SELECT * FROM cache
|
||||
WHERE key = ?
|
||||
''', [key]);
|
||||
if(res.isEmpty){
|
||||
if (res.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
var row = res.first;
|
||||
var dir = row[1] as String;
|
||||
var name = row[2] as String;
|
||||
var expires = row[3] as int;
|
||||
var file = File('$cachePath/$dir/$name');
|
||||
if(await file.exists()){
|
||||
var now = DateTime.now().millisecondsSinceEpoch;
|
||||
if (expires < now) {
|
||||
// expired
|
||||
_db.execute('''
|
||||
DELETE FROM cache
|
||||
WHERE key = ?
|
||||
''', [key]);
|
||||
if (await file.exists()) {
|
||||
await file.delete();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
if (await file.exists()) {
|
||||
// update time
|
||||
var expires = now + 7 * 24 * 60 * 60 * 1000;
|
||||
_db.execute('''
|
||||
UPDATE cache
|
||||
SET expires = ?
|
||||
WHERE key = ?
|
||||
''', [expires, key]);
|
||||
return file;
|
||||
} else {
|
||||
_db.execute('''
|
||||
DELETE FROM cache
|
||||
WHERE key = ?
|
||||
''', [key]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
bool _isChecking = false;
|
||||
|
||||
/// Check cache size and delete expired cache.
|
||||
/// Only check cache if current size is greater than limit size.
|
||||
void checkCacheIfRequired() {
|
||||
if(_currentSize != null && _currentSize! > _limitSize){
|
||||
if (_currentSize != null && _currentSize! > _limitSize) {
|
||||
checkCache();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> checkCache() async{
|
||||
if(_isChecking){
|
||||
/// Check cache size and delete expired cache.
|
||||
/// If current size is greater than limit size,
|
||||
/// delete cache until current size is less than limit size.
|
||||
Future<void> checkCache() async {
|
||||
if (_isChecking) {
|
||||
return;
|
||||
}
|
||||
_isChecking = true;
|
||||
@@ -134,11 +136,13 @@ class CacheManager {
|
||||
SELECT * FROM cache
|
||||
WHERE expires < ?
|
||||
''', [DateTime.now().millisecondsSinceEpoch]);
|
||||
for(var row in res){
|
||||
for (var row in res) {
|
||||
var dir = row[1] as String;
|
||||
var name = row[2] as String;
|
||||
var file = File('$cachePath/$dir/$name');
|
||||
if(await file.exists()){
|
||||
if (await file.exists()) {
|
||||
var size = await file.length();
|
||||
_currentSize = _currentSize! - size;
|
||||
await file.delete();
|
||||
}
|
||||
}
|
||||
@@ -147,26 +151,18 @@ class CacheManager {
|
||||
WHERE expires < ?
|
||||
''', [DateTime.now().millisecondsSinceEpoch]);
|
||||
|
||||
int count = 0;
|
||||
var res2 = _db.select('''
|
||||
SELECT COUNT(*) FROM cache
|
||||
''');
|
||||
if(res2.isNotEmpty){
|
||||
count = res2.first[0] as int;
|
||||
}
|
||||
|
||||
while((_currentSize != null && _currentSize! > _limitSize) || count > 2000){
|
||||
while (_currentSize != null && _currentSize! > _limitSize) {
|
||||
var res = _db.select('''
|
||||
SELECT * FROM cache
|
||||
ORDER BY expires ASC
|
||||
limit 10
|
||||
''');
|
||||
for(var row in res){
|
||||
for (var row in res) {
|
||||
var key = row[0] as String;
|
||||
var dir = row[1] as String;
|
||||
var name = row[2] as String;
|
||||
var file = File('$cachePath/$dir/$name');
|
||||
if(await file.exists()){
|
||||
if (await file.exists()) {
|
||||
var size = await file.length();
|
||||
await file.delete();
|
||||
_db.execute('''
|
||||
@@ -174,7 +170,7 @@ class CacheManager {
|
||||
WHERE key = ?
|
||||
''', [key]);
|
||||
_currentSize = _currentSize! - size;
|
||||
if(_currentSize! <= _limitSize){
|
||||
if (_currentSize! <= _limitSize) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
@@ -183,18 +179,18 @@ class CacheManager {
|
||||
WHERE key = ?
|
||||
''', [key]);
|
||||
}
|
||||
count--;
|
||||
}
|
||||
}
|
||||
_isChecking = false;
|
||||
}
|
||||
|
||||
Future<void> delete(String key) async{
|
||||
/// Delete cache by key.
|
||||
Future<void> delete(String key) async {
|
||||
var res = _db.select('''
|
||||
SELECT * FROM cache
|
||||
WHERE key = ?
|
||||
''', [key]);
|
||||
if(res.isEmpty){
|
||||
if (res.isEmpty) {
|
||||
return;
|
||||
}
|
||||
var row = res.first;
|
||||
@@ -202,7 +198,7 @@ class CacheManager {
|
||||
var name = row[2] as String;
|
||||
var file = File('$cachePath/$dir/$name');
|
||||
var fileSize = 0;
|
||||
if(await file.exists()){
|
||||
if (await file.exists()) {
|
||||
fileSize = await file.length();
|
||||
await file.delete();
|
||||
}
|
||||
@@ -210,11 +206,12 @@ class CacheManager {
|
||||
DELETE FROM cache
|
||||
WHERE key = ?
|
||||
''', [key]);
|
||||
if(_currentSize != null) {
|
||||
if (_currentSize != null) {
|
||||
_currentSize = _currentSize! - fileSize;
|
||||
}
|
||||
}
|
||||
|
||||
/// Delete all cache.
|
||||
Future<void> clear() async {
|
||||
await Directory(cachePath).delete(recursive: true);
|
||||
Directory(cachePath).createSync(recursive: true);
|
||||
@@ -223,75 +220,4 @@ class CacheManager {
|
||||
''');
|
||||
_currentSize = 0;
|
||||
}
|
||||
|
||||
Future<void> deleteKeyword(String keyword) async{
|
||||
var res = _db.select('''
|
||||
SELECT * FROM cache
|
||||
WHERE key LIKE ?
|
||||
''', ['%$keyword%']);
|
||||
for(var row in res){
|
||||
var key = row[0] as String;
|
||||
var dir = row[1] as String;
|
||||
var name = row[2] as String;
|
||||
var file = File('$cachePath/$dir/$name');
|
||||
var fileSize = 0;
|
||||
if(await file.exists()){
|
||||
fileSize = await file.length();
|
||||
try {
|
||||
await file.delete();
|
||||
}
|
||||
finally {}
|
||||
}
|
||||
_db.execute('''
|
||||
DELETE FROM cache
|
||||
WHERE key = ?
|
||||
''', [key]);
|
||||
if(_currentSize != null) {
|
||||
_currentSize = _currentSize! - fileSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CachingFile{
|
||||
CachingFile._(this.key, this.dir, this.name, this.file);
|
||||
|
||||
final String key;
|
||||
|
||||
final String dir;
|
||||
|
||||
final String name;
|
||||
|
||||
final File file;
|
||||
|
||||
final List<int> _buffer = [];
|
||||
|
||||
Future<void> writeBytes(List<int> data) async{
|
||||
_buffer.addAll(data);
|
||||
if(_buffer.length > 1024 * 1024){
|
||||
await file.writeAsBytes(_buffer, mode: FileMode.append);
|
||||
_buffer.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> close() async{
|
||||
if(_buffer.isNotEmpty){
|
||||
await file.writeAsBytes(_buffer, mode: FileMode.append);
|
||||
}
|
||||
CacheManager()._db.execute('''
|
||||
INSERT OR REPLACE INTO cache (key, dir, name, expires) VALUES (?, ?, ?, ?)
|
||||
''', [key, dir, name, DateTime.now().millisecondsSinceEpoch + 7 * 24 * 60 * 60 * 1000]);
|
||||
CacheManager().checkCacheIfRequired();
|
||||
}
|
||||
|
||||
Future<void> cancel() async{
|
||||
await file.deleteIgnoreError();
|
||||
}
|
||||
|
||||
void reset() {
|
||||
_buffer.clear();
|
||||
if(file.existsSync()) {
|
||||
file.deleteSync();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user