-
Notifications
You must be signed in to change notification settings - Fork 1
feat: 从服务器导入文件(引用模式) #167
Copy link
Copy link
Open
Description
背景
用户上传大文件时,通过浏览器上传可能很慢或失败。如果文件已经在服务器上,应该可以直接引用导入,而不是重新上传。
⚠️ 前置条件
此功能需要等待 #166 (Workspace feature) 合并后再开始实现。
原因:
- 文件导入需要关联到当前 workspace
- 依赖
files.workspace_id字段和 workspace 切换机制
需求
在 workspace 中增加"从服务器导入"功能,允许用户直接引用服务器本地目录中的空间数据文件,无需上传。
设计决策
| 决策 | 结论 |
|---|---|
| 目录归属 | 全局数据目录(环境变量配置,默认 ./data/) |
| 权限 | 所有 workspace member 可操作 |
| 文件处理 | 仅引用(不复制,节省磁盘和时间) |
| UI 入口 | 独立按钮"从服务器导入" |
UI 设计
文件列表页
┌─────────────────────────────────────────────────┐
│ MapFlow [工作空间 ▼] [⚙️] │
├─────────────────────────────────────────────────┤
│ │
│ 📁 文件列表 │
│ ┌────────────────────────────────────────────┐ │
│ │ 📄 chengdu_districts.geojson ✅ 已就绪 │ │
│ │ 📄 sichuan_roads.pmtiles ✅ 已就绪 │ │
│ └────────────────────────────────────────────┘ │
│ │
│ [📤 上传文件] [💾 从服务器导入] ← 独立按钮 │
└─────────────────────────────────────────────────┘
点击"从服务器导入"后弹出
┌─────────────────────────────────────────────────┐
│ 💾 从服务器导入 ✕ │
├─────────────────────────────────────────────────┤
│ ⚠️ 文件将被引用,不会被复制。原文件变更会影响数据。│
├─────────────────────────────────────────────────┤
│ 📂 /data/maps [🔍] │
│ ──────────────────────────────────────────── │
│ 📁 china_province/ > │
│ 📁 sichuan_cities/ > │
│ ☑ 📄 chengdu_buildings.geojson 128 MB │
│ ☑ 📄 roads_2024.shp.zip 2.1 GB │
│ ☐ 📄 census_2020.geojson 45 MB │
│ │
│ 已选择 2 个文件 (共 2.2 GB) │
├─────────────────────────────────────────────────┤
│ [取消] [导入选中文件] │
└─────────────────────────────────────────────────┘
导入后
┌─────────────────────────────────────────────────┐
│ 📄 chengdu_buildings.geojson │
│ 状态: 🔄 处理中 │
│ 来源: 📂 /data/maps (引用) │
└─────────────────────────────────────────────────┘
API 设计
1. 获取可导入目录列表
GET /api/server-files/directories
Response:
{
"directories": [
{ "path": "/data/maps", "name": "maps" },
{ "path": "/data/backup", "name": "backup" }
]
}2. 浏览目录内容
GET /api/server-files/browse?path=/data/maps
Response:
{
"currentPath": "/data/maps",
"parentPath": "/data",
"items": [
{ "name": "china_province", "type": "directory" },
{ "name": "sichuan_cities", "type": "directory" },
{ "name": "chengdu_buildings.geojson", "type": "file", "size": 134217728, "ext": ".geojson" },
{ "name": "roads_2024.shp.zip", "type": "file", "size": 2254857830, "ext": ".zip" }
]
}3. 导入文件(引用)
POST /api/server-files/import
Request:
{
"files": [
{ "path": "/data/maps/chengdu_buildings.geojson" },
{ "path": "/data/maps/roads_2024.shp.zip" }
]
}Response:
{
"imported": [
{ "id": "uuid-1", "name": "chengdu_buildings", "path": "/data/maps/chengdu_buildings.geojson", "status": "processing" },
{ "id": "uuid-2", "name": "roads_2024", "path": "/data/maps/roads_2024.shp.zip", "status": "uploaded" }
]
}行为边界
安全
| 规则 | 描述 |
|---|---|
| 路径白名单 | 只能访问配置的数据目录及其子目录 |
| 路径遍历防护 | 拒绝 ..、符号链接跳出白名单 |
| 文件类型验证 | 与上传一致(.geojson, .zip, .mbtiles, .pmtiles 等) |
| 权限检查 | 必须登录且属于当前 workspace |
文件处理
| 规则 | 描述 |
|---|---|
| 引用模式 | 文件路径直接存入 files.path,不复制 |
| 相对路径 | 存储相对于数据目录的路径,便于迁移 |
| 原文件检测 | 导入时检查文件是否存在且可读 |
| 状态流转 | uploaded → processing → ready/failed(与上传一致) |
错误处理
| 场景 | 响应 |
|---|---|
| 路径不在白名单 | 403 Forbidden |
| 文件不存在 | 404 Not Found |
| 文件类型不支持 | 400 Bad Request |
| 原文件被删除/移动 | 瓦片请求返回 404,状态标记为 unavailable |
配置
# 环境变量
SERVER_DATA_DIRS=/data/maps,/data/backup # 逗号分隔的白名单目录数据库变更
-- files 表增加字段标记来源
ALTER TABLE files ADD COLUMN source_type VARCHAR DEFAULT 'upload';
-- source_type: 'upload' | 'server_import'测试用例
后端测试
#[test]
async fn test_import_server_file_success() {
// 导入白名单内的文件 → 成功
}
#[test]
async fn test_import_file_outside_whitelist() {
// 尝试导入白名单外的文件 → 403
}
#[test]
async fn test_import_with_path_traversal() {
// 尝试使用 .. 跳出白名单 → 403
}
#[test]
async fn test_import_unsupported_file_type() {
// 尝试导入 .txt 文件 → 400
}
#[test]
async fn test_import_nonexistent_file() {
// 文件路径不存在 → 404
}
#[test]
async fn test_browse_directory() {
// 浏览目录 → 返回正确的文件/文件夹列表
}
#[test]
async fn test_workspace_isolation() {
// 导入的文件只对当前 workspace 可见
}E2E 测试
test '可以从服务器导入文件', async ({ page }) => {
// 1. 点击"从服务器导入"按钮
// 2. 浏览目录
// 3. 选择文件
// 4. 确认导入
// 5. 验证文件出现在列表中
});相关
- feat: implement workspace feature for multi-tenant data isolation #166 - Workspace feature(PR,需先合并)
- 行为契约将添加到
docs/dev/behaviors.md
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels