Skip to content

fix: binary file download fails with 0KB in WebUI mode + add context menu download#1324

Merged
piorpua merged 2 commits intoiOfficeAI:mainfrom
JerryLiu369:fix/binary-file-download-in-webui
Mar 17, 2026
Merged

fix: binary file download fails with 0KB in WebUI mode + add context menu download#1324
piorpua merged 2 commits intoiOfficeAI:mainfrom
JerryLiu369:fix/binary-file-download-in-webui

Conversation

@JerryLiu369
Copy link
Copy Markdown
Member

@JerryLiu369 JerryLiu369 commented Mar 16, 2026

问题描述 / Problem

在 WebUI 模式下,从预览面板下载 Excel/xlsx、PDF、PPT、Word 等二进制文件时,
下载的文件始终为 0KB,无法正常使用。

此外,工作区文件树没有直接下载选项,导致 .zip 等无法预览的二进制文件完全无法下载。

根本原因 / Root Cause

问题一(0KB):
打开二进制文件预览时,程序将 content 设为空字符串 ""(因为二进制无法以 UTF-8 读取),
预览依赖 filePath,但下载时执行的是 new Blob([content]),content 为空,导致 0 字节文件。

问题二(WebSocket binary 损坏):
WebUI 模式下 WebSocket bridge 会把 ArrayBuffer 强制转为 UTF-8 字符串,
导致 readFileBuffer 无法安全传输二进制数据。
fetch(data: URL) 的方案又被 CSP 的 connect-src 策略拦截。

解决方案 / Solution

利用已有的 ipcBridge.fs.getImageBase64 接口读取任意文件为 base64 字符串
(JSON 字符串传输,对 WebSocket 完全安全),再在浏览器内存中用 atob() + Uint8Array
解码为二进制,最后以正确 MIME 类型创建 Blob 触发下载。

此方案在 Electron 桌面端和 WebUI 模式下均可正常工作。

变更内容 / Changes

Commit 1 — fix: binary file download fails with 0KB in WebUI mode

  • PreviewPanel.tsx:为 excel / pdf / ppt / word 类型增加专用下载分支,
    通过 getImageBase64 + atob() 读取文件二进制,用正确 MIME 类型下载

Commit 2 — feat: add download option to workspace file context menu

  • useWorkspaceFileOps.ts:新增 handleDownloadFile 方法
  • workspace/index.tsx:右键菜单增加"下载"按钮(对所有文件类型生效,包括 .zip 等)
  • 6 种语言 i18n 翻译(en-US / zh-CN / zh-TW / ja-JP / ko-KR / tr-TR)

测试 / Testing

  • 下载 xlsx 文件,文件完整可打开
  • 下载 pdf 文件,文件完整可打开
  • 右键 .zip 文件 → 点击下载,文件完整
  • md / txt 等文本文件下载不受影响
  • Electron 桌面端下载不受影响

@piorpua piorpua self-requested a review March 16, 2026 13:52
JerryLiu369 and others added 2 commits March 17, 2026 10:41
**Problem:**
When downloading Excel/xlsx (and other binary files like PDF, PPT, Word)
from the preview panel in WebUI mode, the downloaded file is always 0KB.

**Root Cause:**
Two compounding issues:

1. When a binary file is opened in the preview panel, `content` is set to
   an empty string `""` (since binary files cannot be loaded as UTF-8 text).
   The preview components use `metadata.filePath` instead. The download
   handler however did `new Blob([content])`, producing an empty 0-byte blob.

2. The naive fix of using `ipcBridge.fs.readFileBuffer` doesn't work in
   WebUI mode because the WebSocket bridge serializes ArrayBuffer to a
   UTF-8 string, corrupting binary data. Using `fetch(dataUrl)` on a
   `data:` URL is also blocked by CSP (`connect-src` does not allow `data:`).

**Fix:**
Add a dedicated branch in `handleDownload` for binary content types
(excel, pdf, ppt, word) when `metadata.filePath` is available:

- Use `ipcBridge.fs.getImageBase64` to read the file as a base64-encoded
  data URL (this IPC already exists, transfers as a plain JSON string,
  and is safe over WebSocket — no binary corruption).
- Decode the base64 in-memory using `atob()` + `Uint8Array` (no `fetch()`
  call, so no CSP violation).
- Create a `Blob` with the correct MIME type for the file extension.

This approach works in both Electron desktop and WebUI modes.

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
Add a "Download" button to the right-click context menu in the workspace
file tree, allowing users to download any file directly without going
through the preview panel first.

This is especially important for binary files (zip, tar.gz, etc.) that
cannot be previewed — previously there was no way to download them from
the WebUI at all. Even for previewable binary files (xlsx, pdf, etc.),
this provides a more direct download path.

Implementation:
- Add `handleDownloadFile` to `useWorkspaceFileOps` hook, using the same
  `getImageBase64` + `atob()` approach as the preview panel fix to ensure
  binary-safe transfer over WebUI's WebSocket bridge.
- Add download button to the context menu (visible for all files).
- Add i18n translations for all 6 supported locales (en-US, zh-CN,
  zh-TW, ja-JP, ko-KR, tr-TR).

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@JerryLiu369 JerryLiu369 force-pushed the fix/binary-file-download-in-webui branch from 53f268a to c7c38e1 Compare March 17, 2026 02:41
Copy link
Copy Markdown
Contributor

@piorpua piorpua left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

Verdict: ✅ Approved with follow-up commits

修复方案正确,已在本地跟进以下改进:


Changes Applied

1. Extracted shared base64ToBlob utility (src/renderer/utils/base64.ts)

Moved the duplicated atob decode logic and MIME_MAP into a shared module, eliminating the DRY violation across PreviewPanel.tsx and useWorkspaceFileOps.ts.

export function base64ToBlob(dataUrl: string, mimeType: string): Blob { ... }
export const BINARY_MIME_MAP: Record<string, string> = { xlsx: '...', pdf: '...', ... };

2. Fixed MIME type resolution in useWorkspaceFileOps.ts

Changed from regex-extracting the MIME from the data: URL prefix (which returns application/octet-stream for binary files) to extension-based lookup via BINARY_MIME_MAP, consistent with PreviewPanel.tsx.

3. Removed console.error residual (useWorkspaceFileOps.ts:479)

4. Added unit tests (tests/unit/base64.test.ts) — 6 tests, all passing.


The core fix (using getImageBase64 + in-memory atob decode to bypass CSP connect-src) is sound and correctly targets the root cause.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants