缩略图 isThumbnail 标志位与命名规范

December 19, 2025
2 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.

背景

在缩略图文件夹中点击缩略图预览时,发现显示的是源文件而非缩略图本身,查看详情时信息也不准确。这是因为缩略图和普通文件共用同一个 FileMetadata 模型,UI 层无法区分。

此外,缩略图和源文件同名,先下载源文件再下载缩略图会导致覆盖。

设计决策

方案对比

方案描述优点缺点
A: 标志位FileMetadata 添加 isThumbnail 字段信息随文件走,健壮性高每个文件多 1 bool
B: 参数传递预览页面传 isThumbnail 参数不改模型需要在多处传参,易漏
C: 检查路径根据 isInThumbsFolder 判断改动小耦合路径逻辑

选择方案 A,原因:

  1. 内存代价可忽略(1 bool/文件)
  2. 代码自解释,不怕漏传参数
  3. 未来新功能自动感知

命名规范后端化

缩略图命名加 _ 前缀的逻辑放在后端实现,原因:

  • 前端换 UI 不需要重新实现
  • 符合”核心逻辑后端化”的设计原则

实现

1. FileMetadata 添加 isThumbnail 字段

class FileMetadata {
  // ... 现有字段
  final bool isThumbnail;  // 默认 false
}

2. 后端 listThumbnails 返回 name 字段

type ThumbInfo struct {
    ID   string    `json:"id"`
    Name string    `json:"name"`   // 新增
    Size int64     `json:"size"`
    Time time.Time `json:"time"`
}
 
// 加载 metadata 获取源文件名,加 _ 前缀
name := "_" + id
if fileMeta, ok := meta.Files[id]; ok {
    name = "_" + fileMeta.Name
}

3. 前端根据 isThumbnail 选择 API

// ImagePreviewPage
final result = widget.file.isThumbnail
    ? await appState.api.getThumbnail(widget.file.id)
    : await appState.api.downloadFile(widget.file.id);
 
// 下载逻辑同理
final result = job.file.isThumbnail
    ? await api.getThumbnail(job.file.id)
    : await api.downloadFile(job.file.id);

4. 详情对话框区分显示

  • 图标:缩略图用 Icons.photo_size_select_actual(青色)
  • 类型:显示”缩略图”而非 mimeType
  • ID 标签:显示”源文件ID”而非”文件ID”
  • 隐藏”加密后大小”(缩略图没有此信息)

附带优化

标题栏只显示当前文件夹名

// 之前:显示完整路径 /文档/工作/
// 现在:只显示 工作
String path = state.currentPath;
if (path.endsWith('/')) {
    path = path.substring(0, path.length - 1);
}
final folderName = path.substring(path.lastIndexOf('/') + 1);
return Text(folderName);

行为变化总结

场景之前现在
点击缩略图预览显示源文件显示缩略图本身
下载缩略图下载源文件下载缩略图本身
缩略图文件名与源文件同名_ + 源文件名
缩略图详情类型显示 image/jpeg类型显示”缩略图”
文件夹标题完整路径只显示文件夹名

经验总结

  1. 标志位 vs 参数传递:当信息需要在多个组件间流转时,标志位更健壮
  2. 后端化命名规则:减少前端逻辑,方便换 UI
  3. 模型变更注意事项:新增字段时 fromJson 需要安全回退(如 json['name'] ?? json['id']