Files
sgxt_web/docs/麒麟系统文件下载兼容性修复方案.md
2026-04-28 11:26:26 +08:00

210 lines
6.5 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 麒麟系统浏览器文件下载兼容性修复方案
## 一、问题现象
**下载成功,但文件打不开**。文件已保存到本地,但用对应软件打开时提示损坏或格式错误。
## 二、根因分析
### 可能原因 1`URL.revokeObjectURL` 释放过早(文件内容损坏)
当前代码第300-313行
```javascript
function downloadFile(url, filename) {
fetch(url)
.then((response) => response.blob())
.then((blob) => {
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
URL.revokeObjectURL(link.href); // 同步释放,可能过早
})
}
```
`link.click()` 在麒麟浏览器中是异步的,同步调用 `revokeObjectURL` 会在 Blob 数据写入磁盘前就销毁它,导致文件内容不完整。
---
### 可能原因 2文件名无后缀 / 后缀被篡改
分析文件名传递链路:
**上传时**`handlerSuccess` 保存的是 `{ id, name }``name` 来自浏览器原始文件名(带后缀),这部分没问题。
**但回显时**watch 中第183-188行存在一个关键问题
```javascript
// 当 modelValue 元素是字符串(非对象)时:
} else {
return {
url: String(`/mosty-api/mosty-base/minio/image/download/` + el || ""),
id: el
// ← 没有 name 属性!
};
}
```
此时 `file.name``undefined`,传入 `downloadFile(file.url, file.name)`
```javascript
link.download = undefined; // 文件名丢失
```
**麒麟浏览器的行为**:当 `download` 属性为空或 `undefined` 时,浏览器会:
- 从 URL 路径提取文件名(如 `/minio/image/download/abc123` → 文件名变成 `abc123`**无后缀**
- 或根据 Blob 的 `type` 自动添加后缀(如服务端返回的 Content-Type 是 `application/json` → 强制加 `.json` 后缀)
**结果**:一个 `.docx` 文件下载后变成了 `.json` 或无后缀文件,自然打不开。
**验证方法**:在麒麟系统上下载一个文件,查看下载后的文件名是否和原始文件名一致(包括后缀)。
---
### 可能原因 3麒麟系统安全机制拦截
麒麟系统(基于 Linux自带安全中心可能触发以下行为
| 安全机制 | 行为 | 结果 |
|----------|------|------|
| 文件隔离 | 将下载的文件标记为不可信,移到隔离区 | 文件存在但被锁,其他程序无法读取 |
| 执行权限 | 给文件添加可执行标记,或删除可执行标记 | 程序拒绝打开带危险标记的文件 |
| 杀毒扫描 | 实时扫描下载文件,误报则隔离 | 文件被移动或内容被修改 |
| WINE 兼容层 | 试图用 WINE 打开 Windows 格式文件 | 文件关联错误,打开方式不对 |
**验证方法**
1. 在麒麟系统终端执行 `ls -la` 查看下载文件是否有特殊权限标记
2. 检查 `/tmp` 或隔离区目录是否有被拦截的文件
3. 暂时关闭麒麟安全中心,重新下载测试
---
## 三、修复方案
### 方案 A综合修复推荐
同时解决原因1和原因2并在下载失败时给出明确提示
```javascript
import { saveAs } from 'file-saver'
// 补全文件名后缀
function ensureFilename(file) {
if (file.name) return file.name
// name 丢失时,从 URL 中尝试提取,或使用默认名
const urlId = file.url?.split('/').pop()
return urlId ? `文件_${urlId}` : '未命名文件'
}
function downloadFile(url, filename) {
fetch(url)
.then((response) => {
if (!response.ok) {
throw new Error(`下载失败: ${response.status}`)
}
return response.blob()
})
.then((blob) => {
// saveAs 内部处理了 Blob 释放时序,不会过早 revoke
saveAs(blob, filename)
})
.catch((error) => {
console.error('下载失败:', error)
ElMessage.error('文件下载失败,请重试')
})
}
const handleDownload = (file) => {
if (file?.response?.data) {
window.open(file.response.data)
} else if (file?.url) {
const filename = ensureFilename(file)
downloadFile(file.url, filename)
}
}
```
**改动内容**
1. 引入 `file-saver`(项目已有依赖)→ 解决 Blob 释放过早
2. `ensureFilename` 补全文件名 → 解决后缀丢失
3. 响应状态校验 → 发现服务端错误时提示用户
---
### 方案 B仅修复 Blob 释放时序(最小改动)
```javascript
function downloadFile(url, filename) {
fetch(url)
.then((response) => response.blob())
.then((blob) => {
const blobUrl = URL.createObjectURL(blob);
const link = document.createElement("a");
link.href = blobUrl;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
// 延迟释放,确保浏览器完成 Blob 数据拷贝
setTimeout(() => URL.revokeObjectURL(blobUrl), 3000);
})
.catch((error) => console.error("下载失败:", error));
}
```
**改动量**1 行。但未修复文件名丢失问题。
---
### 方案 Cwindow.open 直接下载
```javascript
const handleDownload = (file) => {
if (file?.url) {
window.open(file.url, "_blank");
}
};
```
**前提**:后端接口需设置 `Content-Disposition: attachment; filename="xxx.docx"` 响应头,浏览器才能正确处理文件名和下载行为。
---
## 四、排查步骤
建议按以下顺序验证,定位到底是哪个原因:
1. **检查文件名**:在麒麟系统下载后,文件名是否带正确后缀(如 `.docx``.pdf`
- 后缀丢失/被改 → **原因2文件名问题**
- 后缀正确 → 排除原因2
2. **检查文件大小**:下载的文件大小是否和服务端一致?
- 文件明显偏小或0字节 → **原因1Blob 释放过早)**
- 大小一致 → 排除原因1
3. **关闭安全中心测试**:暂时关闭麒麟安全中心,重新下载
- 能正常打开 → **原因3安全拦截**
- 仍打不开 → 排除原因3
---
## 五、方案对比
| | 方案 A 综合修复 | 方案 B 延迟释放 | 方案 C window.open |
|---|---|---|---|
| 修复原因1Blob释放 | ✅ | ✅ | 不涉及 |
| 修复原因2文件名后缀 | ✅ | ❌ | 取决于后端 |
| 处理原因3安全拦截 | ❌ 需系统配置 | ❌ | ❌ |
| 改动量 | ~15行 | 1行 | 最小 |
**建议**先执行排查步骤确认根因再选择对应方案。如果原因1和2同时存在直接用方案A。
---
**文档版本**: v3.0
**创建日期**: 2026-04-24