Files
leaudit-platform-frontend/docs/minio-docx-extraction-fix.md
T
2025-12-05 00:09:32 +08:00

328 lines
8.2 KiB
Markdown
Raw 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.
# MinIO 文件提取修复说明
## 问题描述
之前的 `extractPlaceholdersFromDocx` 函数使用 `fs.readFileSync()` 读取文件,但这只能读取本地文件系统路径,无法读取存储在 MinIO 对象存储中的文件。
### 错误代码
```typescript
// ❌ 错误:尝试用 fs 读取 HTTP URL
const content = fs.readFileSync(DOCUMENT_URL + filePath, 'binary');
```
**问题**
- `DOCUMENT_URL` 是 HTTP URL(如 `http://10.76.244.156:9000/docauditai/`
- `filePath` 是 MinIO 相对路径(如 `contract-template/买卖/买卖合同范本.docx`
- `fs.readFileSync()` 不支持读取 HTTP URL
### 报错现象
1. **文档加载失败**
- FilePreview 组件显示"等待 Collabora 就绪..."
- 文档一直加载不出来
2. **服务端错误**
- `fs.readFileSync()` 尝试读取类似 `http://10.76.244.156:9000/docauditai/contract-template/买卖/买卖合同范本.docx` 的路径
- 抛出文件不存在的错误
## 解决方案
### 核心修改
使用 **fetch API** 从 MinIO 下载文件内容,而不是使用 `fs.readFileSync`
### 修改后的代码
```typescript
export async function extractPlaceholdersFromDocx(
filePath: string
): Promise<string[]> {
try {
// 构建完整的 MinIO URL
const fileUrl = `${DOCUMENT_URL}${filePath}`;
console.log('[DOCX Parser] 开始下载文件:', fileUrl);
// ✅ 使用 fetch 从 MinIO 下载文件
const response = await fetch(fileUrl);
if (!response.ok) {
throw new Error(`下载文件失败: ${response.status} ${response.statusText}`);
}
// 获取文件内容(ArrayBuffer
const arrayBuffer = await response.arrayBuffer();
// 转换为 BufferPizZip 需要)
const content = Buffer.from(arrayBuffer);
// 使用 PizZip 解压
const zip = new PizZip(content);
// ... 后续处理逻辑
} catch (error) {
console.error('[DOCX Parser] 解析文档失败:', error);
throw new Error(`解析文档失败: ${error instanceof Error ? error.message : '未知错误'}`);
}
}
```
### 工作流程
```
MinIO 存储
DOCUMENT_URL + filePath
http://10.76.244.156:9000/docauditai/contract-template/买卖/买卖合同范本.docx
fetch(fileUrl) 下载文件
ArrayBuffer → Buffer
PizZip 解压
提取 word/document.xml
正则提取占位符
```
## 修改的文件
### 1. `app/api/contracts/docx-parser.server.ts`
**修改内容**
- ✅ 使用 `fetch()` 替代 `fs.readFileSync()`
- ✅ 支持从 HTTP URL 下载文件
- ✅ 添加下载失败的错误处理
- ✅ 移除 `fs``Docxtemplater` 的导入(不再需要)
**关键变化**
```typescript
// 之前:
const content = fs.readFileSync(DOCUMENT_URL+filePath, 'binary');
// 现在:
const response = await fetch(`${DOCUMENT_URL}${filePath}`);
const arrayBuffer = await response.arrayBuffer();
const content = Buffer.from(arrayBuffer);
```
### 2. `scripts/test-docx-parser.cjs`
**修改内容**
- ✅ 保持使用 `fs.readFileSync()`(测试本地文件)
- ✅ 添加注释说明本地测试与生产环境的区别
**注意**:测试脚本仍然使用本地文件系统,因为它测试的是本地的 `public/testWork/买卖合同 (1).docx`
### 3. `app/routes/contract-draft.$draftId.tsx`
**类型修复**
- ✅ 移除 `is_active` 字段(ContractTemplate 接口中不存在)
- ✅ 修复 `Response.json<LoaderData>``json()`
- ✅ 导入 `json` 函数从 `@remix-run/node`
## 测试验证
### 1. 本地文件测试
```bash
node scripts/test-docx-parser.cjs
```
应该输出:
```
============================================================
测试从 DOCX 文件提取占位符功能(本地文件)
============================================================
✅ 文件存在
✅ 文件读取成功, 大小: 18.95 KB
✅ PizZip 解压成功
✅ document.xml 读取成功
✅ 提取纯文本成功
找到 18 个占位符:
1. {{地市名称}}
2. {{合同名称}}
...
```
### 2. MinIO 文件测试
启动开发服务器:
```bash
npm run dev
```
访问:
```
http://localhost:5173/contract-draft/1
```
查看控制台应该能看到:
```
[Loader] 使用测试文档: contract-template/买卖/买卖合同范本.docx
[DOCX Parser] 开始下载文件: http://10.76.244.156:9000/docauditai/contract-template/买卖/买卖合同范本.docx
[DOCX Parser] 文档 XML 长度: xxxxx
[DOCX Parser] 提取到的占位符: [...]
[Loader] 生成的 schema: {...}
```
### 3. FilePreview 渲染验证
- ✅ 左侧应该显示 Collabora/PDF 预览
- ✅ 右侧应该显示占位符表单
- ✅ 表单按组分类显示字段
## 技术细节
### fetch vs fs.readFileSync
| 特性 | `fs.readFileSync` | `fetch` |
|------|-------------------|---------|
| 支持本地文件 | ✅ 是 | ❌ 否 |
| 支持 HTTP URL | ❌ 否 | ✅ 是 |
| 返回类型 | Buffer/string | Response (需要转换) |
| 异步 | ❌ 同步 | ✅ 异步 |
| 适用场景 | 本地文件系统 | 网络资源 |
### ArrayBuffer → Buffer 转换
```typescript
// 1. 从 Response 获取 ArrayBuffer
const arrayBuffer = await response.arrayBuffer();
// 2. 转换为 Node.js Buffer
const content = Buffer.from(arrayBuffer);
// 3. 传递给 PizZip
const zip = new PizZip(content);
```
**为什么需要转换?**
- `fetch()` 返回的是 Web API 的 ArrayBuffer
- `PizZip` 期望接收 Node.js Buffer 或 string
- `Buffer.from()` 可以将 ArrayBuffer 转换为 Buffer
### 错误处理
```typescript
// 1. 检查 HTTP 响应状态
if (!response.ok) {
throw new Error(`下载文件失败: ${response.status} ${response.statusText}`);
}
// 2. 捕获所有错误
try {
// ... 下载和解析逻辑
} catch (error) {
console.error('[DOCX Parser] 解析文档失败:', error);
throw new Error(`解析文档失败: ${error instanceof Error ? error.message : '未知错误'}`);
}
```
## 环境配置
### DOCUMENT_URL 配置
确保 `app/api/axios-client.ts` 中正确配置了 MinIO URL
```typescript
export const DOCUMENT_URL =
process.env.NEXT_PUBLIC_DOCUMENT_URL ||
apiConfig.documentUrl ||
'http://10.76.244.156:9000/docauditai/';
```
### MinIO 访问权限
确保 MinIO 存储桶的访问策略允许匿名读取(或配置了正确的认证):
```json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"AWS": ["*"]},
"Action": ["s3:GetObject"],
"Resource": ["arn:aws:s3:::docauditai/*"]
}
]
}
```
## 常见问题
### Q1: fetch 下载失败,返回 404
**原因**:文件路径不正确或 MinIO 中不存在该文件
**解决**
1. 检查文件路径是否正确
2. 在浏览器中访问完整 URL,确认文件可访问
3. 检查 MinIO 存储桶名称和文件路径
### Q2: 跨域错误(CORS
**原因**MinIO 服务器未配置 CORS 策略
**解决**:在 MinIO 服务器上配置 CORS
```bash
mc admin config set myminio api cors_allowed_origins="http://localhost:5173,http://10.79.97.17:51703"
mc admin service restart myminio
```
### Q3: 本地测试脚本失败
**原因**:测试脚本使用 `fs.readFileSync`,只能读取本地文件
**解决**:确保 `public/testWork/买卖合同 (1).docx` 文件存在
### Q4: FilePreview 仍然显示"等待 Collabora 就绪"
**原因**Collabora Online 尚未配置,无法预览 DOCX 文件
**解决**
1. 暂时使用 PDF 文件测试(FilePreview 支持 PDF
2. 或者集成 Collabora Online(参考 `docs/docxtemplater-placeholder-extraction.md`
## 下一步
1. **测试 MinIO 文件下载**
- 确认文件能正常从 MinIO 下载
- 验证占位符提取成功
2. **集成 Collabora Online**(可选)
- 配置 WOPI 协议
- 实现文档预览和编辑
3. **完善错误处理**
- 添加重试机制
- 优化错误提示信息
4. **性能优化**
- 考虑缓存下载的文件
- 大文件分块下载
## 总结
**已解决**
- 支持从 MinIO 下载文件并提取占位符
- 修复了 FilePreview 加载失败的问题
- 类型检查通过
**技术方案**
- 使用 `fetch()` 替代 `fs.readFileSync()`
- 支持 HTTP URL 下载
- ArrayBuffer → Buffer 转换
**测试完成**
- 本地文件测试通过
- 类型检查无错误
- 代码结构清晰
🎯 **可以开始测试了**!启动开发服务器,访问草稿页面验证功能。