# WebSocket 文件管理API文档
## 概述
通过WebSocket连接实现CAD文件的列表查询和下载功能。支持Creo、PDMS、Revit三种CAD软件的文件格式。
## WebSocket连接
**连接地址**: `ws://localhost:8000/api/v1/ws/connect`
**连接参数**:
- `client_id`: 客户端ID(可选,不提供会自动生成)
- `user_id`: 用户ID(可选)
**连接示例**:
```javascript
const ws = new WebSocket('ws://localhost:8000/api/v1/ws/connect?user_id=user123');
```
---
## 支持的文件格式
### 📐 Creo 文件
- `.prt` - 零件文件(Part)
- `.asm` - 装配文件(Assembly)
- `.drw` - 工程图文件(Drawing)
- **特别支持版本号后缀**:`.prt.1`, `.prt.2`, `.asm.1` 等
### 🏭 PDMS 文件
- `.rvm` - 3D模型文件
- `.dri` - 图纸文件
### 🏗️ Revit 文件
- `.rvt` - Revit项目文件
- `.rfa` - Revit族文件(Family)
- `.rte` - Revit模板文件
---
## WebSocket消息格式
### 1. 获取CAD文件列表
**发送消息**:
```json
{
"type": "get_file_list"
}
```
**响应消息**:
```json
{
"type": "info",
"message": "获取文件列表成功",
"data": {
"base_path": "C:\\Users\\Public\\Documents",
"total_count": 15,
"files": [
{
"filename": "part1.prt.1",
"relative_path": "creo/part1.prt.1",
"absolute_path": "C:\\Users\\Public\\Documents\\creo\\part1.prt.1",
"size": 1024000,
"modified_time": 1706950800.0,
"extension": ".prt.1"
},
{
"filename": "building.rvt",
"relative_path": "revit/building.rvt",
"absolute_path": "C:\\Users\\Public\\Documents\\revit\\building.rvt",
"size": 5120000,
"modified_time": 1706951000.0,
"extension": ".rvt"
}
]
},
"timestamp": "2026-02-04T10:30:00"
}
```
---
### 2. 获取单个文件下载链接
**发送消息**:
```json
{
"type": "download_file",
"file_path": "creo/part1.prt.1"
}
```
**响应消息**:
```json
{
"type": "info",
"message": "文件下载链接已生成",
"data": {
"file_path": "creo/part1.prt.1",
"filename": "part1.prt.1",
"download_url": "/api/v1/files/download/creo/part1.prt.1",
"file_size": 1024000
},
"timestamp": "2026-02-04T10:30:00"
}
```
**使用下载链接**:
```javascript
// 获取到download_url后,使用HTTP GET请求下载
const downloadUrl = `http://localhost:8000${data.download_url}`;
window.open(downloadUrl, '_blank');
```
---
### 3. 获取批量下载链接
**发送消息**:
```json
{
"type": "download_batch",
"file_paths": [
"creo/part1.prt.1",
"creo/assembly.asm",
"revit/building.rvt"
]
}
```
**响应消息**:
```json
{
"type": "info",
"message": "批量下载链接已生成",
"data": {
"file_count": 3,
"file_paths": [
"creo/part1.prt.1",
"creo/assembly.asm",
"revit/building.rvt"
],
"download_url": "/api/v1/files/download/batch",
"method": "POST",
"note": "请使用POST方法,将file_paths作为JSON数组发送到此URL"
},
"timestamp": "2026-02-04T10:30:00"
}
```
**使用批量下载**:
```javascript
// 使用fetch API进行POST请求
const response = await fetch('http://localhost:8000/api/v1/files/download/batch', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data.file_paths)
});
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'cad_files.zip';
a.click();
```
---
## 完整前端示例
### JavaScript/WebSocket
```javascript
class CADFileManager {
constructor(serverUrl = 'ws://localhost:8000/api/v1/ws/connect') {
this.ws = null;
this.serverUrl = serverUrl;
this.httpBaseUrl = 'http://localhost:8000';
}
// 连接WebSocket
connect(userId = 'user123') {
return new Promise((resolve, reject) => {
this.ws = new WebSocket(`${this.serverUrl}?user_id=${userId}`);
this.ws.onopen = () => {
console.log('WebSocket连接成功');
resolve();
};
this.ws.onerror = (error) => {
console.error('WebSocket错误:', error);
reject(error);
};
this.ws.onmessage = (event) => {
const message = JSON.parse(event.data);
this.handleMessage(message);
};
});
}
// 处理服务器消息
handleMessage(message) {
console.log('收到消息:', message);
if (message.type === 'info' && message.data) {
// 处理不同类型的响应
if (message.data.files) {
this.onFileListReceived(message.data);
} else if (message.data.download_url) {
this.onDownloadUrlReceived(message.data);
}
} else if (message.type === 'error') {
console.error('错误:', message.message);
}
}
// 获取文件列表
getFileList() {
this.ws.send(JSON.stringify({
type: 'get_file_list'
}));
}
// 文件列表回调(可以被覆盖)
onFileListReceived(data) {
console.log('文件列表:', data.files);
console.log('总数:', data.total_count);
}
// 请求单个文件下载
requestDownload(filePath) {
this.ws.send(JSON.stringify({
type: 'download_file',
file_path: filePath
}));
}
// 下载URL回调
onDownloadUrlReceived(data) {
const fullUrl = `${this.httpBaseUrl}${data.download_url}`;
console.log('下载链接:', fullUrl);
// 自动触发下载
window.open(fullUrl, '_blank');
}
// 请求批量下载
async requestBatchDownload(filePaths) {
// 批量下载直接使用HTTP POST
const response = await fetch(`${this.httpBaseUrl}/api/v1/files/download/batch`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(filePaths)
});
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'cad_files.zip';
a.click();
window.URL.revokeObjectURL(url);
}
// 断开连接
disconnect() {
if (this.ws) {
this.ws.close();
}
}
}
// 使用示例
async function main() {
const manager = new CADFileManager();
// 连接
await manager.connect('user123');
// 自定义文件列表回调
manager.onFileListReceived = (data) => {
console.log(`找到 ${data.total_count} 个文件`);
data.files.forEach(file => {
console.log(`- ${file.filename} (${(file.size / 1024).toFixed(2)} KB)`);
});
};
// 获取文件列表
manager.getFileList();
// 下载单个文件
setTimeout(() => {
manager.requestDownload('creo/part1.prt.1');
}, 2000);
// 批量下载
setTimeout(async () => {
await manager.requestBatchDownload([
'creo/part1.prt.1',
'revit/building.rvt'
]);
}, 4000);
}
main();
```
---
### React示例
```jsx
import React, { useState, useEffect, useRef } from 'react';
function CADFileManager() {
const [files, setFiles] = useState([]);
const [selectedFiles, setSelectedFiles] = useState([]);
const [connected, setConnected] = useState(false);
const wsRef = useRef(null);
useEffect(() => {
// 连接WebSocket
const ws = new WebSocket('ws://localhost:8000/api/v1/ws/connect?user_id=user123');
ws.onopen = () => {
console.log('WebSocket连接成功');
setConnected(true);
// 连接成功后立即获取文件列表
ws.send(JSON.stringify({ type: 'get_file_list' }));
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.type === 'info' && message.data?.files) {
setFiles(message.data.files);
} else if (message.type === 'info' && message.data?.download_url) {
// 自动打开下载链接
window.open(`http://localhost:8000${message.data.download_url}`, '_blank');
}
};
wsRef.current = ws;
return () => {
ws.close();
};
}, []);
// 刷新文件列表
const refreshFileList = () => {
if (wsRef.current && connected) {
wsRef.current.send(JSON.stringify({ type: 'get_file_list' }));
}
};
// 下载单个文件
const downloadFile = (filePath) => {
if (wsRef.current && connected) {
wsRef.current.send(JSON.stringify({
type: 'download_file',
file_path: filePath
}));
}
};
// 批量下载
const downloadBatch = async () => {
const response = await fetch('http://localhost:8000/api/v1/files/download/batch', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(selectedFiles)
});
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'cad_files.zip';
a.click();
window.URL.revokeObjectURL(url);
};
// 切换文件选择
const toggleFileSelection = (filePath) => {
setSelectedFiles(prev =>
prev.includes(filePath)
? prev.filter(f => f !== filePath)
: [...prev, filePath]
);
};
return (
CAD文件管理器
连接状态: {connected ? '✅ 已连接' : '❌ 未连接'}
文件列表 (共 {files.length} 个)
);
}
export default CADFileManager;
```
---
## 错误处理
**错误响应格式**:
```json
{
"type": "error",
"message": "错误描述",
"timestamp": "2026-02-04T10:30:00"
}
```
**常见错误**:
- `"缺少参数: file_path"` - 未提供文件路径
- `"缺少参数: file_paths (必须是数组)"` - 批量下载参数格式错误
- `"访问被拒绝"` - 文件路径不安全
- `"文件不存在"` - 请求的文件不存在
- `"只能下载CAD文件"` - 尝试下载非CAD格式文件
- `"获取文件列表失败"` - 扫描文件系统失败
---
## 注意事项
1. **WebSocket用于控制,HTTP用于下载**
- 文件列表通过WebSocket返回
- 文件下载使用HTTP接口(因为WebSocket不适合传输大文件)
2. **路径安全**
- 所有文件路径都会进行安全检查
- 只能访问配置目录内的文件
3. **文件格式限制**
- 只能下载Creo、PDMS、Revit格式的文件
- 自动识别带版本号的Creo文件
4. **批量下载**
- 文件打包为ZIP格式
- 保持原有目录结构
- 自动跳过不存在或不安全的文件
5. **配置路径**
- 默认路径:`C:\Users\Public\Documents`
- 可在 `configs/software_config.yaml` 中修改
---
## 文件批量重命名功能
### 4. 获取重命名策略列表
**发送消息**:
```json
{
"type": "get_rename_strategies"
}
```
**响应消息**:
```json
{
"type": "info",
"message": "获取重命名策略成功",
"data": {
"strategies": [
{
"name": "add_prefix",
"description": "在文件名前添加前缀"
},
{
"name": "add_suffix",
"description": "在扩展名前添加后缀"
},
{
"name": "sequence",
"description": "使用序号重命名文件"
},
{
"name": "replace_text",
"description": "替换文件名中的文本"
},
{
"name": "add_datetime",
"description": "添加日期时间戳"
},
{
"name": "change_case",
"description": "转换文件名大小写"
}
],
"total_count": 6
},
"timestamp": "2026-02-04T11:10:00"
}
```
---
### 5. 预览重命名结果
**发送消息**:
```json
{
"type": "preview_rename",
"file_paths": [
"creo/part1.prt.1",
"creo/assembly.asm",
"revit/building.rvt"
],
"strategy": "add_prefix",
"params": {
"prefix": "NEW_"
}
}
```
**响应消息**:
```json
{
"type": "info",
"message": "重命名预览生成成功",
"data": {
"strategy": "add_prefix",
"params": {
"prefix": "NEW_"
},
"preview": [
{
"original": "part1.prt.1",
"new": "NEW_part1.prt.1",
"success": true
},
{
"original": "assembly.asm",
"new": "NEW_assembly.asm",
"success": true
},
{
"original": "building.rvt",
"new": "NEW_building.rvt",
"success": true
}
],
"total_count": 3
},
"timestamp": "2026-02-04T11:10:00"
}
```
---
### 6. 执行批量重命名
**发送消息**:
```json
{
"type": "rename_files",
"file_paths": [
"creo/part1.prt.1",
"creo/assembly.asm"
],
"strategy": "sequence",
"params": {
"base_name": "component",
"start_number": 1,
"digits": 3,
"separator": "_"
}
}
```
**响应消息**:
```json
{
"type": "info",
"message": "批量重命名完成: 成功 2 个, 失败 0 个",
"data": {
"strategy": "sequence",
"params": {
"base_name": "component",
"start_number": 1,
"digits": 3,
"separator": "_"
},
"success_count": 2,
"failed_count": 0,
"results": [
{
"original_path": "creo/part1.prt.1",
"original_name": "part1.prt.1",
"new_name": "component_001.prt.1",
"new_path": "creo/component_001.prt.1",
"success": true
},
{
"original_path": "creo/assembly.asm",
"original_name": "assembly.asm",
"new_name": "component_002.asm",
"new_path": "creo/component_002.asm",
"success": true
}
]
},
"timestamp": "2026-02-04T11:10:00"
}
```
---
## 重命名策略参数说明
### 1. add_prefix(添加前缀)
```json
{
"strategy": "add_prefix",
"params": {
"prefix": "NEW_"
}
}
```
### 2. add_suffix(添加后缀)
```json
{
"strategy": "add_suffix",
"params": {
"suffix": "_backup"
}
}
```
### 3. sequence(序号重命名)
```json
{
"strategy": "sequence",
"params": {
"base_name": "file",
"start_number": 1,
"digits": 3,
"separator": "_"
}
}
```
- `base_name`: 基础文件名
- `start_number`: 起始序号(默认1)
- `digits`: 序号位数(默认3,如001)
- `separator`: 分隔符(默认下划线)
### 4. replace_text(文本替换)
```json
{
"strategy": "replace_text",
"params": {
"search": "old",
"replace": "new",
"case_sensitive": true
}
}
```
- `search`: 要搜索的文本
- `replace`: 替换为的文本
- `case_sensitive`: 是否区分大小写(默认true)
### 5. add_datetime(添加日期时间)
```json
{
"strategy": "add_datetime",
"params": {
"format": "%Y%m%d_%H%M%S",
"position": "suffix",
"separator": "_"
}
}
```
- `format`: 日期时间格式(默认:`%Y%m%d_%H%M%S`)
- `position`: 位置,`prefix`或`suffix`(默认suffix)
- `separator`: 分隔符(默认下划线)
### 6. change_case(大小写转换)
```json
{
"strategy": "change_case",
"params": {
"case_type": "lower"
}
}
```
- `case_type`: 转换类型,可选值:`lower`(小写)、`upper`(大写)、`title`(标题格式)
---
## 重命名功能使用示例
### JavaScript示例
```javascript
// 扩展之前的 CADFileManager 类
class CADFileManager {
// ... 之前的代码 ...
// 获取重命名策略列表
getRenameStrategies() {
this.ws.send(JSON.stringify({
type: 'get_rename_strategies'
}));
}
// 预览重命名
previewRename(filePaths, strategy, params) {
this.ws.send(JSON.stringify({
type: 'preview_rename',
file_paths: filePaths,
strategy: strategy,
params: params
}));
}
// 执行批量重命名
renameFiles(filePaths, strategy, params) {
this.ws.send(JSON.stringify({
type: 'rename_files',
file_paths: filePaths,
strategy: strategy,
params: params
}));
}
}
// 使用示例
async function renameExample() {
const manager = new CADFileManager();
await manager.connect('user123');
// 1. 获取策略列表
manager.getRenameStrategies();
// 2. 预览重命名(添加前缀)
setTimeout(() => {
manager.previewRename(
['creo/part1.prt.1', 'creo/assembly.asm'],
'add_prefix',
{ prefix: 'V2_' }
);
}, 1000);
// 3. 执行重命名(序号重命名)
setTimeout(() => {
manager.renameFiles(
['creo/part1.prt.1', 'creo/part2.prt.1'],
'sequence',
{
base_name: 'component',
start_number: 1,
digits: 3,
separator: '_'
}
);
}, 3000);
}
```
### React示例
```jsx
function CADFileRenamer() {
const [files, setFiles] = useState([]);
const [selectedFiles, setSelectedFiles] = useState([]);
const [strategies, setStrategies] = useState([]);
const [selectedStrategy, setSelectedStrategy] = useState('');
const [renameParams, setRenameParams] = useState({});
const [previewResults, setPreviewResults] = useState([]);
const wsRef = useRef(null);
useEffect(() => {
const ws = new WebSocket('ws://localhost:8000/api/v1/ws/connect?user_id=user123');
ws.onopen = () => {
// 获取文件列表和策略列表
ws.send(JSON.stringify({ type: 'get_file_list' }));
ws.send(JSON.stringify({ type: 'get_rename_strategies' }));
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.type === 'info') {
if (message.data?.files) {
setFiles(message.data.files);
} else if (message.data?.strategies) {
setStrategies(message.data.strategies);
} else if (message.data?.preview) {
setPreviewResults(message.data.preview);
} else if (message.data?.results) {
// 重命名完成,刷新文件列表
ws.send(JSON.stringify({ type: 'get_file_list' }));
setPreviewResults([]);
setSelectedFiles([]);
}
}
};
wsRef.current = ws;
return () => ws.close();
}, []);
// 预览重命名
const handlePreview = () => {
if (wsRef.current && selectedFiles.length > 0 && selectedStrategy) {
wsRef.current.send(JSON.stringify({
type: 'preview_rename',
file_paths: selectedFiles,
strategy: selectedStrategy,
params: renameParams
}));
}
};
// 执行重命名
const handleRename = () => {
if (wsRef.current && selectedFiles.length > 0 && selectedStrategy) {
if (confirm(`确定要重命名 ${selectedFiles.length} 个文件吗?`)) {
wsRef.current.send(JSON.stringify({
type: 'rename_files',
file_paths: selectedFiles,
strategy: selectedStrategy,
params: renameParams
}));
}
}
};
return (
CAD文件批量重命名
{/* 策略选择 */}
{/* 参数输入(根据策略动态显示) */}
{selectedStrategy === 'add_prefix' && (
setRenameParams({...renameParams, prefix: e.target.value})}
/>
)}
{/* 操作按钮 */}
{/* 预览结果 */}
{previewResults.length > 0 && (
预览结果
{previewResults.map((result, idx) => (
{result.original} → {result.new}
))}
)}
{/* 文件列表 */}
文件列表
);
}
```
---
## 重命名功能注意事项
1. **文件名冲突处理**
- 如果重命名后的文件名已存在,系统会自动添加数字后缀(如 `file_1`, `file_2`)
- 最多尝试1000次,避免无限循环
2. **扩展名保留**
- 所有重命名策略都会保留原有的文件扩展名
- 支持带版本号的扩展名(如 `.prt.1`, `.prt.2`)
3. **预览功能**
- 建议在执行重命名前先使用预览功能
- 预览不会修改实际文件,只返回重命名结果
4. **错误处理**
- 批量重命名时,单个文件失败不会影响其他文件
- 返回结果中会详细说明每个文件的成功/失败状态
5. **安全性**
- 只能重命名配置目录内的CAD文件
- 路径安全检查防止目录遍历攻击
---
## 配置管理功能
### 7. 获取文件配置
**发送消息**:
```json
{
"type": "get_file_config"
}
```
**响应消息**:
```json
{
"type": "info",
"message": "获取文件配置成功",
"data": {
"base_path": "C:\\Users\\Public\\Documents",
"file_extensions": {
"creo": [".prt", ".asm", ".drw"],
"pdms": [".rvm", ".dri"],
"revit": [".rvt", ".rfa", ".rte"]
}
},
"timestamp": "2026-02-04T14:30:00"
}
```
---
### 8. 设置文件基础路径
**发送消息**:
```json
{
"type": "set_base_path",
"base_path": "D:\\CAD_Files"
}
```
**响应消息(成功)**:
```json
{
"type": "info",
"message": "基础路径设置成功",
"data": {
"base_path": "D:\\CAD_Files"
},
"timestamp": "2026-02-04T14:30:00"
}
```
**响应消息(失败)**:
```json
{
"type": "error",
"message": "设置基础路径失败: 路径不存在: D:\\Invalid_Path",
"timestamp": "2026-02-04T14:30:00"
}
```
---
### 9. 设置文件扩展名
**发送消息**:
```json
{
"type": "set_file_extensions",
"file_extensions": {
"creo": [".prt", ".asm", ".drw", ".sec"],
"pdms": [".rvm", ".dri"],
"revit": [".rvt", ".rfa", ".rte"],
"autocad": [".dwg", ".dxf"]
}
}
```
**响应消息(成功)**:
```json
{
"type": "info",
"message": "文件扩展名配置已更新",
"data": {
"file_extensions": {
"creo": [".prt", ".asm", ".drw", ".sec"],
"pdms": [".rvm", ".dri"],
"revit": [".rvt", ".rfa", ".rte"],
"autocad": [".dwg", ".dxf"]
}
},
"timestamp": "2026-02-04T14:30:00"
}
```
**响应消息(失败)**:
```json
{
"type": "error",
"message": "设置文件扩展名失败: 扩展名格式错误: dwg,必须以'.'开头",
"timestamp": "2026-02-04T14:30:00"
}
```
---
## 配置管理功能使用示例
### JavaScript示例
```javascript
// 扩展之前的 CADFileManager 类
class CADFileManager {
// ... 之前的代码 ...
// 获取文件配置
getFileConfig() {
this.ws.send(JSON.stringify({
type: 'get_file_config'
}));
}
// 设置基础路径
setBasePath(basePath) {
this.ws.send(JSON.stringify({
type: 'set_base_path',
base_path: basePath
}));
}
// 设置文件扩展名
setFileExtensions(extensions) {
this.ws.send(JSON.stringify({
type: 'set_file_extensions',
file_extensions: extensions
}));
}
}
// 使用示例
async function configExample() {
const manager = new CADFileManager();
await manager.connect('user123');
// 1. 获取当前配置
manager.getFileConfig();
// 2. 修改基础路径
setTimeout(() => {
manager.setBasePath('D:\\CAD_Files');
}, 1000);
// 3. 修改文件扩展名(添加AutoCAD支持)
setTimeout(() => {
manager.setFileExtensions({
creo: ['.prt', '.asm', '.drw'],
pdms: ['.rvm', '.dri'],
revit: ['.rvt', '.rfa', '.rte'],
autocad: ['.dwg', '.dxf']
});
}, 2000);
}
```
### React示例
```jsx
function CADConfigManager() {
const [basePath, setBasePath] = useState('');
const [fileExtensions, setFileExtensions] = useState({});
const [newBasePath, setNewBasePath] = useState('');
const wsRef = useRef(null);
useEffect(() => {
const ws = new WebSocket('ws://localhost:8000/api/v1/ws/connect?user_id=user123');
ws.onopen = () => {
// 连接成功后获取当前配置
ws.send(JSON.stringify({ type: 'get_file_config' }));
};
ws.onmessage = (event) => {
const message = JSON.parse(event.data);
if (message.type === 'info' && message.data) {
if (message.data.base_path) {
setBasePath(message.data.base_path);
}
if (message.data.file_extensions) {
setFileExtensions(message.data.file_extensions);
}
}
};
wsRef.current = ws;
return () => ws.close();
}, []);
// 修改基础路径
const handleSetBasePath = () => {
if (wsRef.current && newBasePath) {
wsRef.current.send(JSON.stringify({
type: 'set_base_path',
base_path: newBasePath
}));
}
};
// 修改文件扩展名
const handleSetExtensions = (software, extensions) => {
const newExtensions = {
...fileExtensions,
[software]: extensions
};
if (wsRef.current) {
wsRef.current.send(JSON.stringify({
type: 'set_file_extensions',
file_extensions: newExtensions
}));
}
};
return (
CAD配置管理器
当前文件扩展名配置
{JSON.stringify(fileExtensions, null, 2)}
);
}
```
---
## 配置管理功能注意事项
1. **路径验证**
- 设置基础路径前会验证路径是否存在
- 路径必须是有效的目录,不能是文件
2. **扩展名格式**
- 所有扩展名必须以`.`开头
- 扩展名配置必须是字典格式,值必须是数组
3. **配置持久化**
- 所有配置修改会立即写入`configs/software_config.yaml`文件
- 配置修改后会立即生效,影响文件列表和下载功能
4. **权限要求**
- 修改配置需要对配置文件有写入权限
- 建议在管理员权限下运行
5. **配置同步**
- 修改配置后,建议重新获取文件列表以查看变更效果
- 多个客户端同时修改配置时,以最后一次修改为准