generated from dellevin/template
优化项目结构
This commit is contained in:
102
lib/services/epub/epub_stream_service.dart
Normal file
102
lib/services/epub/epub_stream_service.dart
Normal file
@@ -0,0 +1,102 @@
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'package:archive/archive.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
class EpubStreamService {
|
||||
String? _currentBookPath;
|
||||
String? _pendingBookPath;
|
||||
Future<void>? _openBookFuture;
|
||||
|
||||
/// Cached decoded archive for the current book.
|
||||
Archive? _cachedArchive;
|
||||
|
||||
Future<void> warmUp() async {}
|
||||
|
||||
Future<void> openBook(String epubPath) {
|
||||
if (_currentBookPath == epubPath && _cachedArchive != null) {
|
||||
return Future.value();
|
||||
}
|
||||
|
||||
if (_pendingBookPath == epubPath && _openBookFuture != null) {
|
||||
return _openBookFuture!;
|
||||
}
|
||||
|
||||
_pendingBookPath = epubPath;
|
||||
_openBookFuture = _doOpenBook(epubPath);
|
||||
return _openBookFuture!;
|
||||
}
|
||||
|
||||
Future<void> _doOpenBook(String epubPath) async {
|
||||
try {
|
||||
final bytes = await File(epubPath).readAsBytes();
|
||||
_cachedArchive = ZipDecoder().decodeBytes(bytes);
|
||||
_currentBookPath = epubPath;
|
||||
} catch (e) {
|
||||
_currentBookPath = null;
|
||||
_cachedArchive = null;
|
||||
rethrow;
|
||||
} finally {
|
||||
if (_pendingBookPath == epubPath) {
|
||||
_pendingBookPath = null;
|
||||
_openBookFuture = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Read a single file from the currently open EPUB archive.
|
||||
/// Returns the file bytes, or null if not found / no book loaded.
|
||||
Future<Uint8List?> readFileFromEpub({
|
||||
required String targetFilePath,
|
||||
String? epubPath,
|
||||
}) async {
|
||||
if (epubPath != null && epubPath != _currentBookPath) {
|
||||
await openBook(epubPath);
|
||||
}
|
||||
|
||||
if (_currentBookPath == null || _cachedArchive == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final file = _cachedArchive!.findFile(targetFilePath);
|
||||
if (file == null || file.content == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return file.content is Uint8List
|
||||
? file.content as Uint8List
|
||||
: Uint8List.fromList(file.content as List<int>);
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_cachedArchive = null;
|
||||
_currentBookPath = null;
|
||||
_pendingBookPath = null;
|
||||
_openBookFuture = null;
|
||||
}
|
||||
|
||||
String getMimeType(String filePath) {
|
||||
final ext = p.extension(filePath).toLowerCase().replaceAll('.', '');
|
||||
return _mimeTypeMap[ext] ?? 'application/octet-stream';
|
||||
}
|
||||
|
||||
static const _mimeTypeMap = {
|
||||
'html': 'text/html',
|
||||
'htm': 'text/html',
|
||||
'xhtml': 'application/xhtml+xml',
|
||||
'xml': 'application/xml',
|
||||
'css': 'text/css',
|
||||
'jpg': 'image/jpeg',
|
||||
'jpeg': 'image/jpeg',
|
||||
'png': 'image/png',
|
||||
'gif': 'image/gif',
|
||||
'svg': 'image/svg+xml',
|
||||
'webp': 'image/webp',
|
||||
'ttf': 'font/ttf',
|
||||
'otf': 'font/otf',
|
||||
'woff': 'font/woff',
|
||||
'woff2': 'font/woff2',
|
||||
'js': 'application/javascript',
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user