December 20, 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.

背景

游离文件(orphan files)是指存在于 S3 但不在元数据库中的文件。之前的实现逻辑复杂,分多个循环处理不同目录,且加密文件收养后会被错误标记为”文件损坏”。

核心设计

fileID 与加密密钥

每个文件的加密密钥由 masterKey 和 fileID 共同派生:

fileKey = DeriveFileKey(masterKey, fileID)
  • fileID:文件的 UUID,即 S3 路径 files/{uuid}.enc 中的 uuid
  • masterKey:端到端加密主密钥
  • fileKey:实际用于加密文件内容的密钥

这是标准的密码学设计(HKDF),确保每个文件有独立的密钥,互不影响。

尾部嵌入元数据

加密文件尾部存储原始文件名:

[加密内容][加密的元数据 {"name":"photo.jpg"}][4字节长度]

用途:元数据库丢失后仍能恢复有意义的文件名。

重构后的逻辑

1. 游离文件扫描(极简化)

// 一次扫描所有 S3 对象
allObjects, _ := s3.ListAllObjects(ctx, "")
 
for _, obj := range allObjects {
    // 跳过系统目录
    if strings.HasPrefix(obj.Key, ".e2eepan/") || strings.HasPrefix(obj.Key, "thumbs/") {
        continue
    }
    
    isEncrypted := strings.HasSuffix(obj.Key, ".enc")
    
    if isEncrypted {
        // 检查是否在元数据中
        fileID := strings.TrimSuffix(filepath.Base(obj.Key), ".enc")
        if knownFileIDs[fileID] {
            continue  // 不是游离文件
        }
        // 提取真实文件名
        displayName = extractOriginalName(ctx, obj.Key, obj.Size)
    }
    
    orphans = append(orphans, ...)
}

2. 游离文件收养(两种情况)

文件类型处理方式
files/*.enc直接用原 fileID,提取真名,添加元数据
其他(明文或非 files/ 下的 .enc)加密后上传到 files/,添加元数据
if isEncrypted && isInFilesDir {
    // 情况1:直接添加元数据
    fileID = strings.TrimSuffix(filepath.Base(req.Key), ".enc")
    // 提取真名、解密获取原始大小...
} else {
    // 情况2:加密上传
    if isEncrypted {
        // 先解密原文件
    }
    // 加密后上传到 files/
    fileID = uuid.New().String()
    // ...
}

3. 下载游离文件

if strings.HasSuffix(orphanKey, ".enc") {
    // 派生 fileID
    var fileID string
    if strings.HasPrefix(orphanKey, "files/") {
        fileID = strings.TrimSuffix(filepath.Base(orphanKey), ".enc")
    } else {
        fileID = strings.TrimSuffix(orphanKey, ".enc")
    }
    // 解密后返回
} else {
    // 明文直接返回
}

前端适配

  • OrphanFileInfo 包含 encrypted 字段
  • 转换为 FileMetadata 时设置 isOrphan=true, isEncryptedOrphan=encrypted
  • UI 显示”加密的游离文件”或”明文的游离文件”

Debug 页新增功能

添加”游离文件诊断”选项,调用 GET /api/v1/orphans/debug 返回 S3 文件列表和分类信息,便于在移动端排查问题。

安全说明

Q: UUID 是明文存储,会不会泄露密钥?

A: 不会。fileKey = derive(masterKey, fileID) 是单向函数,攻击者知道 fileID 但不知道 masterKey,无法计算出 fileKey。这是标准密码学设计。