# 文件预览应用选择器 — 前端适配文档 ## 概述 文件预览系统类似 Android 的"使用什么应用打开"机制:用户点击文件时,前端根据扩展名查询可用查看器列表,展示选择弹窗,用户可选"仅此一次"或"始终使用"。 ### 应用类型 | type | 说明 | 前端处理方式 | |------|------|-------------| | `builtin` | 前端内置组件 | 根据 `app_key` 路由到内置组件(如 `pdfjs`、`monaco`) | | `iframe` | iframe 内嵌 | 将 `iframe_url_template` 中的 `{file_url}` 替换为文件下载 URL,嵌入 iframe | | `wopi` | WOPI 协议 | 调用 `/file/{id}/wopi-session` 获取 `editor_url`,嵌入 iframe | ### 内置 app_key 映射 前端需要为以下 `app_key` 实现对应的内置预览组件: | app_key | 组件 | 说明 | |---------|------|------| | `pdfjs` | PDF.js 阅读器 | pdf | | `monaco` | Monaco Editor | txt, md, json, py, js, ts, html, css, ... | | `markdown` | Markdown 渲染器 | md, markdown, mdx | | `image_viewer` | 图片查看器 | jpg, png, gif, webp, svg, ... | | `video_player` | HTML5 Video | mp4, webm, ogg, mov, mkv, m3u8 | | `audio_player` | HTML5 Audio | mp3, wav, flac, aac, m4a, opus | > `office_viewer`(iframe)、`collabora`(wopi)、`onlyoffice`(wopi)默认禁用,需管理员在后台启用和配置。 --- ## 文件下载 URL 与 iframe 预览 ### 现有下载流程(两步式) ``` 步骤1: POST /api/v1/file/download/{file_id} → { access_token, expires_in } 步骤2: GET /api/v1/file/download/{access_token} → 文件二进制流 ``` - 步骤 1 需要 JWT 认证,返回一个下载令牌(有效期 1 小时) - 步骤 2 **不需要认证**,用令牌直接下载,**令牌为一次性**,下载后失效 ### 各类型查看器获取文件内容的方式 | type | 获取文件方式 | 说明 | |------|-------------|------| | `builtin` | 前端自行获取 | 前端用 JS 调用下载接口拿到 Blob/ArrayBuffer,传给内置组件渲染 | | `iframe` | 需要公开可访问的 URL | 第三方服务(如 Office Online)会**从服务端拉取文件** | | `wopi` | WOPI 协议自动处理 | 编辑器通过 `/wopi/files/{id}/contents` 获取,前端只需嵌入 `editor_url` | ### builtin 类型 — 前端自行获取 内置组件(pdfjs、monaco 等)运行在前端,直接用 JS 获取文件内容即可: ```typescript // 方式 A:用下载令牌拼 URL(适用于 PDF.js 等需要 URL 的组件) const { access_token } = await api.post(`/file/download/${fileId}`) const fileUrl = `${baseUrl}/api/v1/file/download/${access_token}` // 传给 PDF.js: pdfjsLib.getDocument(fileUrl) // 方式 B:用 fetch + Authorization 头获取 Blob(适用于需要 ArrayBuffer 的组件) const { access_token } = await api.post(`/file/download/${fileId}`) const blob = await fetch(`${baseUrl}/api/v1/file/download/${access_token}`).then(r => r.blob()) // 传给 Monaco: monaco.editor.create(el, { value: await blob.text() }) ``` ### iframe 类型 — `{file_url}` 替换规则 `iframe_url_template` 中的 `{file_url}` 需要替换为一个**外部可访问的文件直链**。 **问题**:当前下载令牌是一次性的,而 Office Online 等服务可能多次请求该 URL。 **当前可行方案**: ```typescript // 1. 创建下载令牌 const { access_token } = await api.post(`/file/download/${fileId}`) // 2. 拼出完整的文件 URL(必须是公网可达的地址) const fileUrl = `${siteURL}/api/v1/file/download/${access_token}` // 3. 替换模板 const iframeSrc = viewer.iframe_url_template.replace( '{file_url}', encodeURIComponent(fileUrl) ) // 4. 嵌入 iframe //