agentic_huge_data_base / wiki
页面 Open WebUI · 9.6 笔记文件附件·DeepWiki 中文全文译文

9.6 · 笔记文件附件(File Attachments in Notes)

多模型对话工作台与知识应用入口 · 本章是 Open WebUI DeepWiki 中文译文的独立章节页,保留原始链接、源码锚点、模块标签和章节层级。

项目Open WebUI 章节9.6 状态全文译文 模块检索、召回与知识系统、界面与交互、频道、笔记与协作、系统架构
源码线索
  • backend/open_webui/routers/files.py
  • backend/open_webui/routers/knowledge.py
  • src/lib/apis/files/index.ts
  • src/lib/components/icons/AdjustmentsHorizontalOutline.svelte
  • src/lib/components/icons/ArrowUpLeft.svelte
  • src/lib/components/notes/NoteEditor.svelte
  • src/lib/components/notes/NoteEditor/Chat.svelte
  • src/lib/components/notes/NoteEditor/Chat/Message.svelte
  • src/lib/components/notes/NoteEditor/Chat/Messages.svelte
  • src/lib/components/notes/NoteEditor/Controls.svelte
模块标签
  • 检索、召回与知识系统
  • 界面与交互
  • 频道、笔记与协作
  • 系统架构
  • 接口与服务契约

中文译文

笔记文件附件(中文译文)

原始 DeepWiki 页面:https://deepwiki.com/open-webui/open-webui/9.6-file-attachments-in-notes
翻译时间:2026-06-09T16:10:23.706Z
翻译模型:deepseek-chat
原文字符数:20870
项目:Open WebUI (open-webui)

---

笔记中的文件附件

相关源文件

以下文件被用作生成此 wiki 页面的上下文:

  • backend/open_webui/routers/files.py
  • backend/open_webui/routers/knowledge.py
  • src/lib/apis/files/index.ts
  • src/lib/components/icons/AdjustmentsHorizontalOutline.svelte
  • src/lib/components/icons/ArrowUpLeft.svelte
  • src/lib/components/notes/NoteEditor.svelte
  • src/lib/components/notes/NoteEditor/Chat.svelte
  • src/lib/components/notes/NoteEditor/Chat/Message.svelte
  • src/lib/components/notes/NoteEditor/Chat/Messages.svelte
  • src/lib/components/notes/NoteEditor/Controls.svelte
  • src/lib/components/notes/NotePanel.svelte

目的与范围

本文档描述了 Open WebUI 笔记功能中的文件附件系统。内容涵盖文件的上传、存储、协作者间同步,以及与笔记中 AI 功能的集成。文件附件使用户能够通过文档、图片、音频和视频文件来丰富笔记内容,并自动提取内容以支持 AI 增强的工作流程。

关于笔记编辑器的富文本编辑功能,请参阅 9.1 TipTap 编辑器架构。关于笔记内容的协作编辑,请参阅 9.3 协作编辑。关于使用文件内容的 AI 集成功能,请参阅 9.4 笔记中的 AI 集成

---

文件附件架构

笔记中的文件附件系统由多个关键组件组成,它们协同工作以提供无缝的文件管理体验。

系统实体关系
graph TB
    User["用户"]
    NoteEditor["NoteEditor.svelte<br/>(主组件)"]
    Controls["Controls.svelte<br/>(设置面板)"]
    Chat["Chat.svelte<br/>(AI 助手)"]

    UploadAPI["uploadFile()<br/>src/lib/apis/files/index.ts"]
    GetFileAPI["getFileById()<br/>src/lib/apis/files/index.ts"]
    UpdateNoteAPI["updateNoteById()<br/>src/lib/apis/notes.ts"]

    FileStorage[("文件存储<br/>(S3/本地)")]
    ContentExtraction["内容提取<br/>服务"]
    SQLDatabase[("SQL 数据库<br/>NoteTable / FilesTable")]

    SocketIO["Socket.IO<br/>实时同步"]

    User -->|"拖放 / 选择"| NoteEditor
    NoteEditor -->|"upload_file_handler()<br/>backend/open_webui/routers/files.py"| UploadAPI
    UploadAPI -->|"存储文件"| FileStorage
    UploadAPI -->|"process_uploaded_file()<br/>backend/open_webui/routers/files.py"| ContentExtraction
    UploadAPI -->|"返回元数据"| NoteEditor

    NoteEditor -->|"getFileById()"| GetFileAPI
    GetFileAPI -->|"检索文件 + 内容"| FileStorage
    GetFileAPI -->|"返回文件对象"| NoteEditor

    NoteEditor -->|"changeDebounceHandler()"| UpdateNoteAPI
    UpdateNoteAPI -->|"保存 note.data.files"| SQLDatabase

    NoteEditor -->|"files 数组"| Controls
    NoteEditor -->|"files 数组"| Chat

    NoteEditor -->|"emit('join-note')"| SocketIO
    SocketIO -->|"noteEventHandler()"| NoteEditor
    SocketIO -.->|"同步文件"| SQLDatabase

    Controls -->|"显示/移除"| User
    Chat -->|"file.file.data.content"| ContentExtraction

来源:src/lib/components/notes/NoteEditor.svelte:101-182src/lib/components/notes/NoteEditor/Controls.svelte:1-40src/lib/components/notes/NoteEditor/Chat.svelte:1-70backend/open_webui/routers/files.py:105-175backend/open_webui/routers/files.py:200-210

---

文件数据结构

附加到笔记的文件遵循特定的数据结构,用于跟踪上传状态、元数据和内容。

文件对象模式
字段类型描述
idstring \null后端分配的唯一文件标识符 backend/open_webui/routers/files.py:177
itemIdstring上传期间的临时 UUID src/lib/components/notes/NoteEditor.svelte:410-411
typestring文件类型('file' 或 'image')src/lib/components/notes/NoteEditor.svelte:413-415
fileobject \string文件元数据和提取的内容 backend/open_webui/models/files.py:32-38
namestring原始文件名 backend/open_webui/models/files.py:32-38
urlstring文件 URL 或 ID 引用
sizenumber文件大小(字节)backend/open_webui/models/files.py:32-38
collection_namestring关联的知识库集合
content_typestringMIME 类型 backend/open_webui/models/files.py:32-38
statusstring上传状态('uploading' 或 'uploaded')
errorstring上传失败时的错误消息 src/lib/components/notes/NoteEditor.svelte:471-477
存储位置

文件存储在笔记的数据结构中:

  • note.data.files - 持久化到数据库的文件对象数组 src/lib/components/notes/NoteEditor.svelte:110-118
  • 每次通过 updateNoteById() 更新笔记时都会包含文件 src/lib/components/notes/NoteEditor.svelte:209-218
  • 来源:src/lib/components/notes/NoteEditor.svelte:408-420src/lib/components/notes/NoteEditor.svelte:209-218backend/open_webui/models/files.py:32-38

---

文件上传流程

文件上传工作流处理文件选择、内容提取以及与协作者的同步。

代码到逻辑的映射
sequenceDiagram
    participant User
    participant NoteEditor as "NoteEditor.svelte"
    participant uploadFileAPI as "uploadFile()<br/>src/lib/apis/files/index.ts"
    participant FilesRouter as "files.py<br/>@router.post('/')"
    participant ProcessFileFunc as "process_uploaded_file()<br/>files.py"
    participant ContentExtraction as "内容提取引擎"
    participant Storage as "存储提供者"
    participant Database as "FilesTable / NoteTable"
    participant SocketIO as "Socket.IO"
    participant Collaborator

    Note over NoteEditor,Collaborator: 两个用户加入笔记房间

    User->>NoteEditor: 拖放 / 选择文件
    NoteEditor->>NoteEditor: "uploadFileHandler(file)"
    NoteEditor->>NoteEditor: "创建临时 fileItem<br/>(itemId=uuidv4())"
    NoteEditor->>NoteEditor: "files.push(fileItem)<br/>(status='uploading')"
    NoteEditor->>NoteEditor: "打开 Controls 面板"

    alt 音频/视频文件
        NoteEditor->>NoteEditor: "设置 metadata.language<br/>(来自 settings.audio.stt.language)"
    end

    NoteEditor->>uploadFileAPI: "uploadFile(token, file, metadata)"
    uploadFileAPI->>FilesRouter: "POST /files/"
    FilesRouter->>Storage: "保存文件到磁盘/云"
    FilesRouter->>ProcessFileFunc: "process_uploaded_file(request, file, file_path, file_item, metadata, user, db)"
    ProcessFileFunc->>ContentExtraction: "提取文本内容(例如,转录音频、OCR 图片)"
    ContentExtraction-->>ProcessFileFunc: "提取的内容"
    ProcessFileFunc->>Database: "用内容和状态更新 file_item"
    FilesRouter-->>uploadFileAPI: "{id, collection_name, error}"

    uploadFileAPI->>NoteEditor: "文件元数据和处理状态"

    NoteEditor->>NoteEditor: "fileItem.status='uploaded'<br/>fileItem.file=file<br/>fileItem.id=id"

    NoteEditor->>Database: "updateNoteById(token, id,<br/>{data: {files}})"
    Database-->>NoteEditor: "成功"

    NoteEditor->>SocketIO: "笔记更新事件"
    SocketIO->>Collaborator: "noteEventHandler()"
    Collaborator->>Collaborator: "同步 files 数组"

来源:src/lib/components/notes/NoteEditor.svelte:407-478src/lib/components/notes/NoteEditor.svelte:201-219backend/open_webui/routers/files.py:105-175backend/open_webui/routers/files.py:200-210src/lib/apis/files/index.ts:4-95

上传实现

uploadFileHandler() 函数实现了完整的上传工作流:

  1. 创建临时项:生成一个 UUID 用于上传期间的跟踪 src/lib/components/notes/NoteEditor.svelte:410-411
  2. 添加到文件数组:立即显示状态为 'uploading' 的文件 src/lib/components/notes/NoteEditor.svelte:419-420
  3. 打开 Controls 面板:自动显示设置面板以便查看 src/lib/components/notes/NoteEditor.svelte:429-434
  4. 提取元数据:对于音频/视频,包含 STT 语言偏好 src/lib/components/notes/NoteEditor.svelte:438-446
  5. 上传文件:调用 uploadFile() API 并传入文件和元数据 src/lib/components/notes/NoteEditor.svelte:449-449。这会调用后端路由 POST /files/ src/lib/apis/files/index.ts:23-30
  6. 内容提取:后端的 process_uploaded_file() 函数根据文件类型和配置的引擎处理内容提取(例如,音频使用 transcribe(),其他使用 process_file()backend/open_webui/routers/files.py:105-175
  7. 检索完整文件:调用 getFileById() 获取包含内容的完整文件对象 src/lib/components/notes/NoteEditor.svelte:460-463
  8. 更新状态:将状态更改为 'uploaded' 并填充文件数据 src/lib/components/notes/NoteEditor.svelte:467-468
  9. 持久化到笔记:通过 changeDebounceHandler() 防抖保存到 note.data.files src/lib/components/notes/NoteEditor.svelte:201-219
  10. 来源:src/lib/components/notes/NoteEditor.svelte:407-478src/lib/components/notes/NoteEditor.svelte:201-219backend/open_webui/routers/files.py:105-175src/lib/apis/files/index.ts:4-95

---

内容提取

文件在上传期间会自动进行内容提取,以支持 AI 功能。

提取能力

内容提取服务处理各种文件类型:

文件类型提取方法输出
音频transcribe() 函数 backend/open_webui/routers/files.py:128-134转录文本
其他process_file() 函数 backend/open_webui/routers/files.py:139-144纯文本
图片OCR(通过 process_file()文本内容
纯文本直接读取(通过 process_file()文本内容

_is_text_file() 辅助函数 backend/open_webui/routers/files.py:70-89 用于检测被错误标记的文本文件(例如,被错误识别为视频的 .ts 文件),并通过读取一个数据块并验证 UTF-8 编码来确保它们被作为纯文本处理 backend/open_webui/routers/files.py:76-88

访问提取的内容

提取的内容可以通过文件对象结构访问:

// 内容嵌套在 file.file.data.content 中
const extractedText = file?.file?.data?.content ?? '无法提取内容';

此内容用于两个主要场景:

  1. AI 聊天上下文:文件为关于笔记的 AI 对话提供上下文 src/lib/components/notes/NoteEditor/Chat.svelte:168-169
  2. 知识库:文件可以添加到知识库中用于 RAG 查询 backend/open_webui/routers/knowledge.py:23-28(通过 process_file)。
  3. 来源:src/lib/components/notes/NoteEditor.svelte:448-449src/lib/components/notes/NoteEditor/Chat.svelte:166-169backend/open_webui/routers/files.py:70-89backend/open_webui/routers/files.py:128-134backend/open_webui/routers/files.py:139-144

---

文件显示与管理

文件在 Controls 面板中显示,并根据文件类型进行不同的渲染。

Controls 面板显示

Controls.svelte 组件在两个部分中渲染文件:

graph LR
    FilesArray["files[] 数组"]

    RegularFiles["常规文件<br/>filter(type !== 'image')"]
    ImageFiles["图片文件<br/>filter(type === 'image' || content_type.startsWith('image/'))"]

    FileItem["FileItem 组件<br/>(带关闭按钮)"]
    ImageComp["图片组件<br/>(缩略图,可关闭)"]

    FilesArray --> RegularFiles
    FilesArray --> ImageFiles

    RegularFiles --> FileItem
    ImageFiles --> ImageComp

    FileItem -->|"on:dismiss"| RemoveFile["从 files[] 中移除"]
    ImageComp -->|"onDismiss"| RemoveFile

    RemoveFile -->|"onUpdate(files)"| UpdateNote["调用 changeDebounceHandler()"]

来源:src/lib/components/notes/NoteEditor/Controls.svelte:40-103

文件组件特性

常规文件src/lib/components/notes/NoteEditor/Controls.svelte:45-68):

  • 显示名称、大小和类型。
  • 显示上传进度指示器 src/lib/components/notes/NoteEditor/Controls.svelte:56-56
  • 可通过 X 按钮关闭 src/lib/components/notes/NoteEditor/Controls.svelte:57-63
  • 紧凑的小型布局。

图片文件src/lib/components/notes/NoteEditor/Controls.svelte:70-84):

  • 14x14 缩略图,带圆角 src/lib/components/notes/NoteEditor/Controls.svelte:74-74
  • 采用 flex wrap 布局排列 src/lib/components/notes/NoteEditor/Controls.svelte:70-70
  • 可关闭的覆盖按钮 src/lib/components/notes/NoteEditor/Controls.svelte:76-82
  • 使用 object-cover 缩放。
文件移除

移除文件会触发一个多步骤流程:

  1. files 数组中过滤掉该文件 src/lib/components/notes/NoteEditor/Controls.svelte:59-59
  2. 调用 onUpdate(files) 回调 src/lib/components/notes/NoteEditor/Controls.svelte:62-62
  3. changeDebounceHandler() 自动持久化到数据库 src/lib/components/notes/NoteEditor.svelte:201-219
  4. 防抖更新防止过多的 API 调用(200ms 延迟)。
  5. 来源:src/lib/components/notes/NoteEditor/Controls.svelte:40-103src/lib/components/notes/NoteEditor.svelte:201-219

---

协作文件同步

文件附件通过 Socket.IO 在所有编辑同一笔记的协作者之间自动同步。

Socket.IO 集成

当加载具有写权限的笔记时(src/lib/components/notes/NoteEditor.svelte:184-192):

  1. 加入笔记房间:使用笔记 ID 和认证令牌发送 join-note 事件 src/lib/components/notes/NoteEditor.svelte:189-194
  2. 注册处理程序:通过 noteEventHandler() 订阅 note-events src/lib/components/notes/NoteEditor.svelte:195-195
  3. 实时更新:接收来自其他协作者的文件更改。
  4. 自动同步:文件数组更新会传播到所有已连接的用户。
同步生命周期
sequenceDiagram
    participant UserA as "用户 A"
    participant EditorA as "NoteEditor A"
    participant Socket as "Socket.IO"
    participant Database as "NoteTable"
    participant EditorB as "NoteEditor B"
    participant UserB as "用户 B"

    Note over EditorA,EditorB: 两个用户加入笔记房间

    EditorA->>Socket: "emit('join-note', {note_id, auth})"
    EditorB->>Socket: "emit('join-note', {note_id, auth})"

    Socket-->>EditorA: "on('note-events', handler)"
    Socket-->>EditorB: "on('note-events', handler)"

    UserA->>EditorA: "上传文件"
    EditorA->>EditorA: "files.push(newFile)"
    EditorA->>Database: "updateNoteById({data: {files}})"

    Database-->>EditorA: "成功"
    EditorA->>Socket: "笔记更新事件"

    Socket->>EditorB: "noteEventHandler()"
    EditorB->>EditorB: "同步 files 数组"
    EditorB->>UserB: "显示新文件"

来源:src/lib/components/notes/NoteEditor.svelte:184-196src/lib/components/notes/NoteEditor.svelte:201-219

---

与 AI 聊天的集成

文件附件与笔记中的 AI 聊天功能深度集成,为 AI 驱动的对话提供上下文。

上下文注入

Chat.svelte 组件构建一个包含文件内容的系统提示(src/lib/components/notes/NoteEditor/Chat.svelte:156-170):

system = `你是一个有用的助手...

<notes>${note?.data?.content?.md ?? ''}</notes>
<context>${files.map(file =>
  `${file.name}: ${file?.file?.data?.content ?? '无法提取内容'}\n`
).join('')}</context>
<selection>${selectedContent?.text}</selection>`;
文档编辑模式

当 AI 编辑启用时(editEnabled = true),文件通过 DEFAULT_DOCUMENT_EDITOR_PROMPT 为文档增强提供补充上下文(src/lib/components/notes/NoteEditor/Chat.svelte:83-102)。

文件内容使用
模式文件内容用途提示类型
编辑模式用文件信息补充笔记内容文档编辑器提示
聊天模式回答关于文件和笔记内容的问题通用助手提示

来源:src/lib/components/notes/NoteEditor/Chat.svelte:156-194src/lib/components/notes/NoteEditor/Chat.svelte:83-102

---

错误处理与边界情况

文件附件系统包含针对各种失败场景的健壮错误处理。

上传错误处理

空文件验证src/lib/components/notes/NoteEditor.svelte:422-426):

if (fileItem.size == 0) {
    toast.error($i18n.t('无法上传空文件。'));
    return null;
}

上传失败恢复src/lib/components/notes/NoteEditor.svelte:471-477):

  • 显示包含异常消息的错误提示。
  • 从文件数组中移除临时文件项。
  • 防止部分文件对象持久化。

后端错误警告src/lib/components/notes/NoteEditor.svelte:454-457):

if (uploadedFile.error) {
    console.warn('文件上传警告:', uploadedFile.error);
    toast.warning(uploadedFile.error);
}
内容提取回退

当内容提取失败时:

  • 文件仍附加到笔记。
  • file?.file?.data?.content 返回 null 或 undefined。
  • AI 聊天使用回退文本:'无法提取内容' src/lib/components/notes/NoteEditor/Chat.svelte:168
  • 用户仍然可以访问和下载原始文件。
  • 来源:src/lib/components/notes/NoteEditor.svelte:422-477src/lib/components/notes/NoteEditor/Chat.svelte:168

---

防抖持久化

文件更改使用防抖机制持久化到数据库,以优化性能。

防抖实现

changeDebounceHandler() 函数实现了 200ms 的防抖(src/lib/components/notes/NoteEditor.svelte:201-219):

let debounceTimeout: NodeJS.Timeout | null = null;

const changeDebounceHandler = () => {
    if (debounceTimeout) {
        clearTimeout(debounceTimeout);
    }

    debounceTimeout = setTimeout(async () => {
        const res = await updateNoteById(localStorage.token, id, {
            title: note?.title === '' ? $i18n.t('无标题') : note.title,
            data: {
                files: files
            },
            access_grants: note?.access_grants ?? []
        });
    }, 200);
};
自动触发

防抖处理程序在以下情况下自动调用:

  • 通过 uploadFileHandler() 上传文件时 src/lib/components/notes/NoteEditor.svelte:477
  • 通过 Controls 面板关闭移除文件时 src/lib/components/notes/NoteEditor/Controls.svelte:62
  • 手动修改文件数组时。
  • 笔记标题发生更改时。
  • 来源:src/lib/components/notes/NoteEditor.svelte:201-219src/lib/components/notes/NoteEditor.svelte:477src/lib/components/notes/NoteEditor/Controls.svelte:62

---

API 集成点

文件附件在其生命周期中与多个后端 API 端点交互。

文件上传 API

函数uploadFile(token, file, metadata, process) 位置src/lib/apis/files/index.ts src/lib/apis/files/index.ts:4-95 参数

  • token:认证令牌。
  • file:用户选择的文件对象。
  • metadata:可选对象,包含用于 STT 的 language
  • process:布尔标志,用于触发提取。
文件检索 API

函数getFileById(token, id) 位置src/lib/apis/files/index.ts src/lib/apis/files/index.ts:216-245 返回:包含提取文本内容的文件对象 src/lib/components/notes/NoteEditor.svelte:460-463

笔记更新 API

函数updateNoteById(token, id, payload) 负载结构

{
    title: string,
    data: {
        files: Array<FileObject>
    },
    access_grants: Array<AccessGrant>
}

来源:src/lib/components/notes/NoteEditor.svelte:27-28src/lib/components/notes/NoteEditor.svelte:449src/lib/components/notes/NoteEditor.svelte:460-463src/lib/components/notes/NoteEditor.svelte:209-218src/lib/apis/files/index.ts:4-95