Bootstrap 初始化迁移 + ThumbnailService 统一服务

January 1, 2026
3 min read
By devshan

Table of Contents

This is a list of all the sections in this post. Click on any of them to jump to that section.

背景

两个历史遗留问题需要解决:

  1. Bootstrap 检查分散问题:bootstrap 状态检查和自动解锁逻辑放在 FilesPage 中,导致我不在文件页时无法触发初始化
  2. 缩略图逻辑分散问题:缩略图相关逻辑分散在 file_tiles.dart、chat_page.dart 等多处,维护困难

Phase 1: Bootstrap 初始化迁移

问题分析

原有逻辑在 FilesPage 中:

  • _bootstrapChecked, _bootstrapChecking, _vaultStatus, _conflictCount 都是 FilesPage 的本地变量
  • _checkBootstrapStatus() 方法在 FilesPage 中实现
  • 使用时必须进入文件页才能触发初始化和自动解锁

解决方案

将所有 bootstrap 相关状态和逻辑迁移到 AppState:

新增状态变量:

// app_state.dart
String? _vaultStatus;      // 'ready', 'empty', 'conflict'
bool _bootstrapChecked = false;
bool _bootstrapChecking = false;
int? _conflictCount;

新增方法:

/// 检查 bootstrap 状态并尝试自动解锁
Future<void> _checkBootstrapAndAutoUnlock() async {
  if (_bootstrapChecked || _bootstrapChecking) return;
  if (_isOffline) return;
  if (_isUnlocked) return;
  
  _bootstrapChecking = true;
  notifyListeners();
  
  // 调用 API 检查状态,尝试自动解锁...
}
 
/// 重置 bootstrap 状态(切换 S3 配置时)
void _resetBootstrapState() { ... }
 
/// 手动触发检查(供 UI 层调用)
Future<void> checkBootstrapIfNeeded() async { ... }

集成点:

  • _checkInitialNetworkStatus() 中调用 _checkBootstrapAndAutoUnlock()
  • switchToS3Config() 中调用 _resetBootstrapState()

FilesPage 清理

删除的内容:

  • 本地状态变量:_bootstrapChecked, _bootstrapChecking, _vaultStatus, _conflictCount
  • 方法:_checkBootstrapStatus()

更新的内容:

  • _onStateChanged() 改为调用 appState.checkBootstrapIfNeeded()
  • UI 渲染改为读取 state.vaultStatus, state.bootstrapChecking, state.conflictCount

Phase 2: ThumbnailService 统一服务

问题分析

缩略图相关逻辑分散在多处:

  • file_tiles.dart: fetchOrGenerateThumbnail(), _generateVideoThumbnail()
  • chat_page.dart: _regenerateThumbnail()
  • 类型判断重复:多处检查 isImage(), isVideo()

解决方案

创建 ThumbnailService 单例服务统一管理:

// core/services/thumbnail_service.dart
class ThumbnailService {
  static final ThumbnailService _instance = ThumbnailService._();
  factory ThumbnailService() => _instance;
  
  // 类型判断
  bool supportsThumbnail(String fileName) { ... }
  bool isImage(String fileName) { ... }
  bool isVideo(String fileName) { ... }
  
  // 统一加载入口
  Future<Uint8List?> loadThumbnail({
    required String fileId,
    String? fileName,
    required ApiClient api,
    bool autoGenImage = false,
    bool autoGenVideo = false,
  }) async {
    // 1. 检查本地缓存
    // 2. 从远程加载
    // 3. 按需自动生成
  }
  
  // 视频缩略图生成
  Future<Uint8List?> generateVideoThumbnailFromStream(...) { ... }
  Future<Uint8List?> generateVideoThumbnailFromFile(...) { ... }
  
  // 手动重新生成
  Future<bool> regenerateThumbnail(...) { ... }
}

调用方简化

file_tiles.dart:

Future<Uint8List?> fetchOrGenerateThumbnail(...) async {
  return ThumbnailService().loadThumbnail(
    fileId: fileId,
    fileName: fileName,
    api: api,
    autoGenImage: autoGenImage,
    autoGenVideo: autoGenVideo,
  );
}

chat_page.dart:

Future<void> _regenerateThumbnail(String fileId, String fileName) async {
  await ThumbnailService().regenerateThumbnail(
    fileId: fileId,
    fileName: fileName,
    api: appState.api,
  );
}

技术细节

缩略图加载流程

loadThumbnail()
├─ ThumbnailCacheManager.loadThumbnail() → 本地磁盘缓存
├─ api.getThumbnailWithInfo(autoGen) → 远程加载
└─ _tryAutoGenerate() → 自动生成
├─ 图片: 后端 autoGen=true 时按需生成
└─ 视频: 前端通过流式 URL 生成

视频缩略图生成

使用 VideoThumbnailService 从流式 URL 生成:

Future<Uint8List?> generateVideoThumbnailFromStream({
  required String fileId,
  required ApiClient api,
}) async {
  final streamUrl = api.getStreamUrl(fileId);
  final token = api.getAuthToken();
  final headers = <String, String>{};
  if (token != null) headers['X-Auth-Token'] = token;
  
  return VideoThumbnailService().generateFromUrl(streamUrl, headers);
}

修改的文件

文件操作说明
app_state.dart修改添加 bootstrap 状态管理
files_page.dart修改清理重复逻辑,改用 AppState
thumbnail_service.dart新增统一缩略图服务
file_tiles.dart修改简化为调用 ThumbnailService
chat_page.dart修改简化为调用 ThumbnailService

验证结果

flutter analyze
Analyzing client...
No issues found!

只剩 3 个预存的 info 级别警告,非本次引入。

收益

  1. 初始化更可靠:bootstrap 检查在 AppState 层面完成,不依赖特定页面
  2. 代码更集中:缩略图逻辑统一到 ThumbnailService,便于维护和扩展
  3. 职责更清晰:UI 层只需调用服务,不再关心底层实现细节