---
# System prepended metadata

title: 文件管理需求&方案

---

# 文件管理需求&方案

## 需求列表

### 文件夹

文件夹封层，每个对象存储当前路径 dir 和 父节点 id ，以及当前文件夹的表示。

nodeType 为节点类型，主要是为了查询根节点。

增加环境参数存储，区分不同环境下的文件夹

##### 文件夹的属性

- 名称
- 标识
- 描述
- ParaentId
- Dir: 当前路径
- nodeType: root | child
- 环境参数
- 创建人
- 更新时间
- 创建时间

##### 文件夹的API

- 文件夹列表（全量）
- 创建文件夹
- 更新文件夹
- 查询文件夹 > 通过id搜索
- 删除文件夹

### 文件

上传至oss之后，存储url、文件名、备注、从属文件夹。文件url不对外公开

##### 文件的属性

- 文件名
- 描述
- key
- 文件夹id
- 创建人
- url
- 更新时间
- 创建时间

##### 文件的API

- 文件列表分页
- 创建文件
- 更新文件
- 删除文件
- 通过id获取文件详情

### 文件访问控制

通过解析路径，查询数据库中存储url，并返回对应的资源

## 方案设计


![](https://i.imgur.com/QfAoi0u.png)

### 数据结构设计

#### 文件夹

folder

```javascript
{
  name: { type: String },
  displayName: { type: String },
  parentId: { type: Schema.Types.ObjectId, ref: 'folder'},
  dir: { type: String }, // 添加index，所属文件夹，如 '/'
  nodeType: { type: String }, // ROOT | CHILD
  env: { type: String }, // 环境参数
  createdBy: { type: Schema.Types.ObjectId, ref: 'User'},
  updatedBy: { type: Schema.Types.ObjectId, ref: 'User'}
}, {
  timestamps: true
}


index
 + paraentId - name - env // 唯一索引
 + dir // 索引，方便查询 getByDir
```



#### 文件

file

```javascript
{
    name: { type: String },
    description: { type: String },
    folder: { type: Schema.Types.ObjectId, ref: 'Folder'},
    url: { type: String }, // oss 存储地址
    createdBy: { type: Schema.Types.ObjectId, ref: 'User'},
    updatedBy: { type: Schema.Types.ObjectId, ref: 'User'},
}, {
  timestamps: true
}

index
	+ folder - name
```

### API设计

文件夹返回对象

FolderOutbound

```javascript
{
	id: string,
	name: string,
	displayName: string,
	parentId: string,
	dir: string, // 所属文件夹
	nodeType: 'ROOT' | 'CHILD',
	env: Env, // 所属环境
	createdBy: User,
	updatedBy: User,
	createdAt: number,
	updatedAt: number
}
```

文件返回对象

FileOutbound

```
{
	id: string,
	name: string,
	description: string,
	folder: string,
	url: string, // 文件虚拟地址，通过 static 服务转发至 oss
	createdAt: string,
	updatedAt: string,
	createdBy: User,
	updatedBy: User
}
```



##### 文件夹列表（全量）

| Method | Url     |      |
| ------ | ------- | ---- |
| get    | /folder |      |

Response: 

```javascript
{
	code: 0,
	msg: '',
	data: FolderOutbound[]
}
```

##### 创建文件夹

| Method | Url     |      |
| ------ | ------- | ---- |
| post   | /folder |      |

Request query

```javascript
{
	env: 'DEV' // 环境
}
```



Request body

```javascript
{
	name: string,
	displayName: string,
	parentId: string, // optional 缺省时，创建根目录下的文件夹
}
```



##### 更新文件夹

重命名，拖动文件夹。

需要注意，如果拖动了文件夹，所属的文件夹dir属性需要更新

| Method | Url         |      |
| ------ | ----------- | ---- |
| put    | /folder/:id |      |

Request param

```javascript
{
	id: ObjectId // 文件夹id
}
```



Request body

```javascript
{
	name: string,
	displayName: string,
	parentId: string, // optional 缺省时，创建根目录下的文件夹
}
```

Response

code :403 - name-existed

code: 0

```javascript
{
	code: 0,
	data: FolderOutbound
}
```

##### 查询文件夹 > 通过id搜索

| Method | Url         |      |
| ------ | ----------- | ---- |
| Get    | /folder/:id |      |

Request param

```javascript
{
	id: ObjectId // 文件夹id
}
```

Response

code: 0

```javascript
{
	code: 0,
	data: FolderOutbound
}
```

##### 删除文件夹

| Method | Url         |      |
| ------ | ----------- | ---- |
| delete | /folder/:id |      |

Request param

```javascript
{
	id: ObjectId // 文件夹id
}
```

Response

code: 0

```
{
	code: 0
}
```

-----------

##### 文件列表全量

| Method | Url   |      |
| ------ | ----- | ---- |
| get    | /file |      |

Request quest

```javascript
{
	folderId: ObjectId // 文件夹id
}
```

Response

code: 0

```
{
	code: 0
	data: FileOutbound[]
}
```

##### 创建文件

| Method | Url   |      |
| ------ | ----- | ---- |
| Post   | /file |      |

Request quest

```javascript
{
	folderId: ObjectId // 文件夹id
  name: string, // 文件名
  description: string, // 文件描述
  url: string // 文件地址
}
```

Response

code: 0

```javascript
{
	code: 0
	data: FileOutbound[]
}
```

##### 更新文件

可以更新文件夹

| Method | Url       |      |
| ------ | --------- | ---- |
| put    | /file/:id |      |

Request param

```javascript
{
	id: fileId
}
```

Request quest

```javascript
{
    folderId: ObjectId // 文件夹id
    name: string, // 文件名
    description: string, // 文件描述
    url: string // 文件地址
}
```

Response

code: 0

```javascript
{
	code: 0
	data: FileOutbound[]
}
```

##### 删除文件

| Method | Url       |      |
| ------ | --------- | ---- |
| delete | /file/:id |      |

Request param

```Javascript
{
	id: fileId
}

```

Response

code: 0

```javascript
{
	code: 0
}
```

##### 通过id获取文件详情

| Method | Url       |      |
| ------ | --------- | ---- |
| get    | /file/:id |      |

Request param

```javascript
{
	id: fileId // 文件id
}
```

Response

code: 0

```javascript
{
	code: 0
	data: FileOutbound[]
}
```

------------------------------

##### 上传文件

###### 图片上传

上传文件至CDN

| Method | Url      |      |
| ------ | -------- | ---- |
| Post   | /jdImage |      |

Request quest

```javascript
{
	files: Joi.binary().description('FormData文件')
}
```

Response

code: 0

```javascript
{
	code: 0
	data: string[] //cdn 地址
}
```

###### 文件上传

上传文件至oss

| Method | Url  |      |
| ------ | ---- | ---- |
| Post   | /oss |      |

Request quest

```javascript
{
	files: Joi.binary().description('FormData文件')
}
```

Response

code: 0

```javascript
{
	code: 0
	data: string[] //oss 地址
}
```



### 文件访问控制

route

/static/app/xxx/folderName/fileName?env=DEV





### 遇到的问题

新建一个服务，使用neos-backend调用该服务，权限验证没有拆出来

#### 


