切换主题
🗑️ 文件删除接口
本文档介绍文件删除相关的所有接口,支持安全可控的文件删除操作。
功能概述
文件删除接口提供了安全可控的文件删除功能,支持:
- ✅ 多种删除方式(路径参数、查询参数、批量删除)
- ✅ 批量删除接口,支持并发删除,一次请求删除多个文件
- ✅ 业务类型隔离,不同业务独立管理
- ✅ 删除权限控制,可配置是否允许删除
- ✅ 路径安全验证,自动清理和验证路径
- ✅ 支持多种存储类型(本地、OSS、COS、OBS、S3、HTTP转发)
- ✅ 自动路径清理,去除多余斜杠和危险字符
- ✅ 详细的错误提示,便于快速定位问题
- ✅ 完善的错误处理和日志记录
删除流程
删除方式
方式对比
| 方式 | URL格式 | 适用场景 | 推荐度 |
|---|---|---|---|
| 路径参数 | POST /delete/{业务类型}/{文件路径} | 标准删除,URL简洁直观 | ⭐⭐⭐⭐⭐ |
| 批量删除 | POST /delete/batch/{业务类型} | 一次删除多个文件,支持并发 | ⭐⭐⭐⭐⭐ |
| 查询参数 | POST /delete/{业务类型}?filePath=xxx | 路径包含特殊字符 | ⭐⭐⭐⭐ |
| 默认删除 | POST /delete?filePath=xxx | 内部系统,不区分业务类型 | ⭐⭐⭐ |
接口说明
1️⃣ 路径参数删除(推荐)
通过URL路径指定业务类型和文件路径,URL更简洁直观。
接口信息
POST /delete/{业务类型}/{文件路径}1
请求参数
| 参数名 | 位置 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|---|
业务类型 | Path | String | 是 | 业务类型代码,需在配置中定义 | document |
文件路径 | Path | String | 是 | 文件存储路径,支持多级目录 | 2024/08/25/file.pdf |
路径格式说明
- 路径会自动清理:去除多余的
/、将\替换为/ - 路径不能包含
..和~等危险字符 - 路径支持中文和特殊字符(建议使用URL编码)
请求示例
bash
# 基本删除
curl -X POST "http://localhost:9830/delete/document/2024/08/25/23/abc123.pdf"
# 带URL编码的路径(包含特殊字符)
curl -X POST "http://localhost:9830/delete/image/2024/08/25/%E5%9B%BE%E7%89%87.jpg"1
2
3
4
5
2
3
4
5
javascript
// 使用fetch
async function deleteFile(businessType, filePath) {
const response = await fetch(`/delete/${businessType}/${filePath}`, {
method: 'POST'
});
const result = await response.json();
if (result.successful) {
console.log('删除成功');
} else {
console.error('删除失败:', result.msg);
}
return result;
}
// 使用示例
deleteFile('document', '2024/08/25/23/abc123.pdf');1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
javascript
// 使用axios
import axios from 'axios';
async function deleteFile(businessType, filePath) {
try {
const response = await axios.post(
`/delete/${businessType}/${filePath}`
);
if (response.data.successful) {
console.log('删除成功');
return true;
} else {
console.error('删除失败:', response.data.msg);
return false;
}
} catch (error) {
console.error('请求失败:', error);
return false;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
响应示例
json
{
"code": 0,
"successful": true,
"msg": null
}1
2
3
4
5
2
3
4
5
json
{
"code": -1000,
"successful": false,
"msg": "该业务类型不允许删除文件"
}1
2
3
4
5
2
3
4
5
json
{
"code": -1000,
"successful": false,
"msg": "文件不存在"
}1
2
3
4
5
2
3
4
5
json
{
"code": -1000,
"successful": false,
"msg": "非法的文件路径"
}1
2
3
4
5
2
3
4
5
json
{
"code": -1000,
"successful": false,
"msg": "业务类型不存在"
}1
2
3
4
5
2
3
4
5
2️⃣ 查询参数删除
通过查询参数指定文件路径,适合路径包含特殊字符的场景。
接口信息
POST /delete/{业务类型}?filePath={文件路径}1
请求参数
| 参数名 | 位置 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|---|
业务类型 | Path | String | 是 | 业务类型代码 | image |
filePath | Query | String | 是 | 文件存储路径 | /2024/08/25/photo.jpg |
请求示例
bash
curl -X POST "http://localhost:9830/delete/image?filePath=/2024/08/25/23/photo.jpg"1
javascript
async function deleteFile(businessType, filePath) {
const params = new URLSearchParams({ filePath });
const response = await fetch(`/delete/${businessType}?${params}`, {
method: 'POST'
});
return await response.json();
}
// 使用示例
deleteFile('image', '/2024/08/25/23/photo.jpg');1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
3️⃣ 批量删除接口(推荐)
通过一次请求删除多个文件,支持并发删除,返回每个文件的删除结果。
接口信息
POST /delete/batch/{业务类型}
POST /delete/batch1
2
2
请求参数
| 参数名 | 位置 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|---|
业务类型 | Path | String | 否 | 业务类型代码,不提供时使用默认业务类型 | document |
filePaths | Body | Array[String] | 是 | 文件路径数组,最多100个 | ["path1", "path2"] |
批量删除特性
- 支持并发删除,默认并发数为5
- 单个文件失败不影响其他文件删除
- 返回每个文件的详细删除结果
- 最多支持100个文件批量删除
- 自动进行路径安全验证和清理
请求示例
bash
curl -X POST "http://localhost:9830/delete/batch/document" \
-H "Content-Type: application/json" \
-d '{
"filePaths": [
"2024/08/25/23/file1.pdf",
"2024/08/25/23/file2.pdf",
"2024/08/25/23/file3.pdf"
]
}'1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
javascript
async function batchDelete(businessType, filePaths) {
const response = await fetch(`/delete/batch/${businessType}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
filePaths: filePaths
})
});
const result = await response.json();
if (result.successful) {
const data = result.data;
console.log(`删除完成: 总数${data.totalCount},成功${data.successCount},失败${data.failedCount}`);
// 处理失败的文件
if (data.failedCount > 0) {
const failedFiles = data.results.filter(r => !r.success);
console.error('删除失败的文件:', failedFiles);
}
} else {
console.error('批量删除失败:', result.msg);
}
return result;
}
// 使用示例
const files = [
'2024/08/25/23/file1.pdf',
'2024/08/25/23/file2.pdf',
'2024/08/25/23/file3.pdf'
];
batchDelete('document', files);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
javascript
import axios from 'axios';
async function batchDelete(businessType, filePaths) {
try {
const response = await axios.post(
`/delete/batch/${businessType}`,
{ filePaths: filePaths }
);
if (response.data.successful) {
const data = response.data.data;
console.log(`删除完成: 成功${data.successCount}/${data.totalCount}`);
return data;
} else {
console.error('批量删除失败:', response.data.msg);
return null;
}
} catch (error) {
console.error('请求失败:', error);
return null;
}
}
// 使用示例
batchDelete('document', [
'2024/08/25/23/file1.pdf',
'2024/08/25/23/file2.pdf'
]);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
响应示例
json
{
"code": 0,
"successful": true,
"data": {
"totalCount": 3,
"successCount": 2,
"failedCount": 1,
"results": [
{
"filePath": "2024/08/25/23/file1.pdf",
"success": true,
"message": ""
},
{
"filePath": "2024/08/25/23/file2.pdf",
"success": true,
"message": ""
},
{
"filePath": "2024/08/25/23/file3.pdf",
"success": false,
"message": "文件不存在"
}
]
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
json
{
"code": -1000,
"successful": false,
"msg": "filePaths参数不能为null,请提供文件路径数组"
}1
2
3
4
5
2
3
4
5
json
{
"code": -1000,
"successful": false,
"msg": "批量删除数量不能超过100个"
}1
2
3
4
5
2
3
4
5
json
{
"code": -1000,
"successful": false,
"msg": "该业务类型不允许删除文件"
}1
2
3
4
5
2
3
4
5
4️⃣ 默认删除接口
不区分业务类型的通用删除接口,适用于内部系统。
接口信息
POST /delete?filePath={文件路径}1
安全警告
此接口不进行业务类型校验,存在安全风险:
- ⚠️ 可能删除任意业务类型的文件
- ⚠️ 缺少业务隔离保护
- ⚠️ 建议仅在内网环境使用
- ⚠️ 生产环境必须添加额外的权限控制
请求参数
| 参数名 | 位置 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|---|
filePath | Query | String | 是 | 文件存储路径 | /2024/08/25/file.pdf |
使用示例
场景1: 单文件删除
javascript
async function deleteSingleFile(businessType, filePath) {
try {
const response = await fetch(`/delete/${businessType}/${filePath}`, {
method: 'POST'
});
const result = await response.json();
if (result.successful) {
alert('文件删除成功');
// 刷新文件列表
refreshFileList();
} else {
alert(`删除失败: ${result.msg}`);
}
} catch (error) {
console.error('删除错误:', error);
alert('删除失败,请稍后重试');
}
}
// 使用示例
deleteSingleFile('document', '2024/08/25/23/report.pdf');1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
场景2: 使用批量删除接口(推荐)
javascript
// ✅ 推荐:使用批量删除接口
async function batchDelete(businessType, filePaths) {
try {
const response = await fetch(`/delete/batch/${businessType}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ filePaths })
});
const result = await response.json();
if (result.successful) {
const data = result.data;
console.log(`删除完成: 总数${data.totalCount},成功${data.successCount},失败${data.failedCount}`);
// 处理失败的文件
if (data.failedCount > 0) {
const failedFiles = data.results.filter(r => !r.success);
console.error('删除失败的文件:', failedFiles);
// 可以显示失败原因
failedFiles.forEach(file => {
console.error(` - ${file.filePath}: ${file.message}`);
});
}
return data;
} else {
console.error('批量删除失败:', result.msg);
return null;
}
} catch (error) {
console.error('请求失败:', error);
return null;
}
}
// 使用示例
const files = [
'2024/08/25/23/file1.pdf',
'2024/08/25/23/file2.pdf',
'2024/08/25/23/file3.pdf'
];
batchDelete('document', files).then(data => {
if (data && data.failedCount > 0) {
alert(`删除完成,但有${data.failedCount}个文件删除失败`);
}
});1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
场景2-1: 使用单文件删除接口批量删除(不推荐)
javascript
// ❌ 不推荐:使用单文件接口循环删除
async function batchDeleteLegacy(businessType, filePaths) {
const results = {
success: [],
failed: []
};
// 串行删除,避免并发过多
for (const filePath of filePaths) {
try {
const response = await fetch(`/delete/${businessType}/${filePath}`, {
method: 'POST'
});
const result = await response.json();
if (result.successful) {
results.success.push(filePath);
} else {
results.failed.push({ filePath, error: result.msg });
}
} catch (error) {
results.failed.push({ filePath, error: error.message });
}
// 添加延迟,避免请求过快
await new Promise(resolve => setTimeout(resolve, 100));
}
console.log(`删除完成: 成功${results.success.length}个, 失败${results.failed.length}个`);
return results;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
场景3: 带确认的删除
javascript
async function deleteWithConfirm(businessType, filePath, fileName) {
// 显示确认对话框
const confirmed = confirm(`确定要删除文件 "${fileName}" 吗?\n此操作不可恢复!`);
if (!confirmed) {
return { cancelled: true };
}
try {
const response = await fetch(`/delete/${businessType}/${filePath}`, {
method: 'POST'
});
const result = await response.json();
if (result.successful) {
alert('文件已删除');
return { success: true };
} else {
alert(`删除失败: ${result.msg}`);
return { success: false, error: result.msg };
}
} catch (error) {
console.error('删除错误:', error);
alert('删除失败,请稍后重试');
return { success: false, error: error.message };
}
}
// 使用示例
deleteWithConfirm('document', '2024/08/25/23/report.pdf', '报告.pdf');1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
场景4: React组件示例
jsx
import React, { useState } from 'react';
function FileDeleteButton({ businessType, filePath, fileName, onDeleted }) {
const [deleting, setDeleting] = useState(false);
const handleDelete = async () => {
if (!window.confirm(`确定要删除 "${fileName}" 吗?\n此操作不可恢复!`)) {
return;
}
setDeleting(true);
try {
const response = await fetch(
`/delete/${businessType}/${filePath}`,
{ method: 'POST' }
);
const result = await response.json();
if (result.successful) {
alert('删除成功');
onDeleted && onDeleted(filePath);
} else {
alert(`删除失败: ${result.msg}`);
}
} catch (error) {
console.error('删除错误:', error);
alert('删除失败,请稍后重试');
} finally {
setDeleting(false);
}
};
return (
<button
onClick={handleDelete}
disabled={deleting}
className="delete-btn"
>
{deleting ? '删除中...' : '删除'}
</button>
);
}
// 使用示例
<FileDeleteButton
businessType="document"
filePath="2024/08/25/23/report.pdf"
fileName="报告.pdf"
onDeleted={(path) => console.log('已删除:', path)}
/>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
场景5: Go客户端示例
go
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
)
type BatchDeleteRequest struct {
FilePaths []string `json:"filePaths"`
}
type BatchDeleteResponse struct {
Code int `json:"code"`
Successful bool `json:"successful"`
Msg string `json:"msg"`
Data struct {
TotalCount int `json:"totalCount"`
SuccessCount int `json:"successCount"`
FailedCount int `json:"failedCount"`
Results []struct {
FilePath string `json:"filePath"`
Success bool `json:"success"`
Message string `json:"message"`
} `json:"results"`
} `json:"data"`
}
func BatchDelete(serverURL, businessType string, filePaths []string) (*BatchDeleteResponse, error) {
url := fmt.Sprintf("%s/delete/batch/%s", serverURL, businessType)
reqBody := BatchDeleteRequest{
FilePaths: filePaths,
}
jsonData, err := json.Marshal(reqBody)
if err != nil {
return nil, err
}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var result BatchDeleteResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, err
}
return &result, nil
}
// 使用示例
func main() {
result, err := BatchDelete(
"http://localhost:9830",
"document",
[]string{
"2024/08/25/23/file1.pdf",
"2024/08/25/23/file2.pdf",
"2024/08/25/23/file3.pdf",
},
)
if err != nil {
fmt.Printf("批量删除失败: %v\n", err)
return
}
if result.Successful {
fmt.Printf("删除完成: 总数%d,成功%d,失败%d\n",
result.Data.TotalCount,
result.Data.SuccessCount,
result.Data.FailedCount)
// 处理失败的文件
if result.Data.FailedCount > 0 {
fmt.Println("删除失败的文件:")
for _, r := range result.Data.Results {
if !r.Success {
fmt.Printf(" - %s: %s\n", r.FilePath, r.Message)
}
}
}
} else {
fmt.Printf("批量删除失败: %s\n", result.Msg)
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
场景6: Vue组件示例
vue
<template>
<div class="file-item">
<span>{{ fileName }}</span>
<el-button
@click="handleDelete"
:loading="deleting"
type="danger"
size="small"
>
{{ deleting ? '删除中...' : '删除' }}
</el-button>
</div>
</template>
<script>
export default {
props: {
businessType: {
type: String,
required: true
},
filePath: {
type: String,
required: true
},
fileName: {
type: String,
required: true
}
},
data() {
return {
deleting: false
};
},
methods: {
async handleDelete() {
try {
await this.$confirm(
`确定要删除 "${this.fileName}" 吗?此操作不可恢复!`,
'删除确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
);
} catch {
return; // 用户取消
}
this.deleting = true;
try {
const response = await fetch(
`/delete/${this.businessType}/${this.filePath}`,
{ method: 'POST' }
);
const result = await response.json();
if (result.successful) {
this.$message.success('删除成功');
this.$emit('deleted', this.filePath);
} else {
this.$message.error(`删除失败: ${result.msg}`);
}
} catch (error) {
console.error('删除错误:', error);
this.$message.error('删除失败,请稍后重试');
} finally {
this.deleting = false;
}
}
}
};
</script>1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
最佳实践
✅ 推荐做法
1. 权限配置
yaml
# config.yaml
files:
- business-type: document
allow-delete: true # 允许删除
- business-type: archive
allow-delete: false # 不允许删除(归档文件)1
2
3
4
5
6
7
2
3
4
5
6
7
2. 删除前确认
javascript
// ✅ 推荐:删除前确认
async function safeDelete(businessType, filePath, fileName) {
const confirmed = confirm(`确定要删除 "${fileName}" 吗?`);
if (!confirmed) return;
await deleteFile(businessType, filePath);
}
// ❌ 不推荐:直接删除
async function unsafeDelete(businessType, filePath) {
await deleteFile(businessType, filePath);
}1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
3. 错误处理
javascript
async function deleteWithErrorHandling(businessType, filePath) {
try {
const response = await fetch(`/delete/${businessType}/${filePath}`, {
method: 'POST'
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const result = await response.json();
if (!result.successful) {
// 根据错误信息进行不同处理
if (result.msg.includes('不允许删除')) {
alert('该文件不允许删除');
} else if (result.msg.includes('不存在')) {
alert('文件不存在或已被删除');
} else {
alert(`删除失败: ${result.msg}`);
}
return false;
}
return true;
} catch (error) {
console.error('删除错误:', error);
alert('网络错误,请稍后重试');
return false;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
4. 批量删除优化
javascript
// ✅ 推荐:使用批量删除接口(服务端已处理并发)
async function batchDeleteOptimized(businessType, filePaths) {
// 批量删除接口已内置并发控制,无需客户端处理
const response = await fetch(`/delete/batch/${businessType}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ filePaths })
});
const result = await response.json();
if (result.successful) {
return result.data;
} else {
throw new Error(result.msg);
}
}
// 如果文件数量超过100个,需要分批处理
async function batchDeleteLarge(businessType, filePaths) {
const batchSize = 100; // 每批最多100个
const results = [];
for (let i = 0; i < filePaths.length; i += batchSize) {
const batch = filePaths.slice(i, i + batchSize);
const result = await batchDeleteOptimized(businessType, batch);
results.push(result);
}
// 汇总结果
const total = {
totalCount: 0,
successCount: 0,
failedCount: 0,
results: []
};
results.forEach(r => {
total.totalCount += r.totalCount;
total.successCount += r.successCount;
total.failedCount += r.failedCount;
total.results.push(...r.results);
});
return total;
}
// 带重试的删除函数
async function deleteFileWithRetry(businessType, filePath, maxRetries = 3) {
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(`/delete/${businessType}/${filePath}`, {
method: 'POST'
});
const result = await response.json();
if (result.successful) {
return result;
}
// 如果是文件不存在,不需要重试
if (result.msg && result.msg.includes('不存在')) {
return result;
}
// 最后一次重试失败,返回结果
if (i === maxRetries - 1) {
return result;
}
// 等待后重试
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
} catch (error) {
if (i === maxRetries - 1) {
return { successful: false, msg: error.message };
}
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
}
// ❌ 不推荐:无限制并发
async function batchDeleteUncontrolled(businessType, filePaths) {
return await Promise.all(
filePaths.map(path => deleteFile(businessType, path))
);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
5. 路径处理
javascript
// ✅ 推荐:使用URL编码处理特殊字符
function deleteFileSafe(businessType, filePath) {
// 对路径进行URL编码
const encodedPath = encodeURIComponent(filePath)
.replace(/%2F/g, '/'); // 保留路径分隔符
return fetch(`/delete/${businessType}/${encodedPath}`, {
method: 'POST'
});
}
// ✅ 推荐:使用查询参数方式(自动处理编码)
function deleteFileWithQuery(businessType, filePath) {
const params = new URLSearchParams({ filePath });
return fetch(`/delete/${businessType}?${params}`, {
method: 'POST'
});
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
⚠️ 注意事项
| 项目 | 说明 |
|---|---|
| 权限控制 | 必须在配置中设置 allow-delete: true 才能删除,否则返回错误 |
| 路径安全 | 自动清理路径(去除多余斜杠),过滤 .. 和 ~ 等危险字符,防止路径遍历攻击 |
| 删除确认 | 建议在客户端添加删除确认对话框,防止误删重要文件 |
| 不可恢复 | 文件删除后无法恢复,请谨慎操作,重要文件建议设置 allow-delete: false |
| 批量删除 | 推荐使用批量删除接口 /delete/batch,服务端已处理并发控制;如需删除超过100个文件,请分批处理 |
| 日志记录 | 服务端会记录删除操作日志(包括业务类型、文件路径、时间),便于审计和排查问题 |
| 业务隔离 | 使用业务类型接口,确保不同业务的数据完全隔离,互不影响 |
| 路径格式 | 路径会自动清理,支持 /path/file 和 //path///file 等多种格式 |
| 错误处理 | 建议根据不同的错误信息进行相应的处理(文件不存在、权限不足等) |
🔒 安全建议
生产环境配置
yamlfiles: - business-type: important-docs allow-delete: false # 重要文档禁止删除1
2
3添加二次确认
javascriptasync function criticalDelete(businessType, filePath, fileName) { const confirm1 = confirm(`确定要删除 "${fileName}" 吗?`); if (!confirm1) return; const confirm2 = confirm('此操作不可恢复,确定继续吗?'); if (!confirm2) return; await deleteFile(businessType, filePath); }1
2
3
4
5
6
7
8
9记录删除日志
javascriptasync function deleteWithLog(businessType, filePath, operator) { // 记录删除操作 await logOperation({ action: 'delete', businessType, filePath, operator, timestamp: new Date().toISOString() }); // 执行删除 return await deleteFile(businessType, filePath); }1
2
3
4
5
6
7
8
9
10
11
12
13
响应字段说明
单文件删除响应
| 字段名 | 类型 | 说明 |
|---|---|---|
code | Number | 错误码,0表示成功 |
successful | Boolean | 操作是否成功 |
msg | String | 错误信息,成功时为null |
批量删除响应
| 字段名 | 类型 | 说明 |
|---|---|---|
code | Number | 错误码,0表示成功 |
successful | Boolean | 操作是否成功 |
data | Object | 批量删除结果数据 |
data.totalCount | Number | 总文件数量 |
data.successCount | Number | 成功删除数量 |
data.failedCount | Number | 失败删除数量 |
data.results | Array | 每个文件的删除结果 |
data.results[].filePath | String | 文件路径 |
data.results[].success | Boolean | 是否删除成功 |
data.results[].message | String | 错误信息(失败时) |
错误码说明
| 错误码 | 说明 | 常见原因 | 解决方案 |
|---|---|---|---|
0 | 成功 | - | - |
-1000 | 业务类型不存在 | 配置文件中没有定义该业务类型 | 检查配置文件,确认业务类型是否正确 |
-1000 | 该业务类型不允许删除文件 | 配置中设置了 allow-delete: false | 修改配置文件允许删除,或使用其他允许删除的业务类型 |
-1000 | 非法的文件路径 | 路径包含 ..、~ 等危险字符 | 使用合法的文件路径,避免路径遍历攻击 |
-1000 | 文件不存在 | 文件已被删除、路径错误或文件从未存在 | 检查文件路径是否正确,确认文件是否已被删除 |
-1000 | 删除失败 | 存储服务异常、网络问题、权限不足 | 查看服务器日志,检查存储服务状态,确认权限配置 |
-1000 | 批量删除数量超限 | 文件数量超过100个 | 分批处理,每批最多100个文件 |
-1000 | 请求参数格式错误 | JSON格式错误或参数缺失 | 检查请求体格式,确保filePaths为数组且不为空 |
常见问题
Q: 删除的文件可以恢复吗?
A: 不可以。文件删除后会从存储系统中永久删除,无法恢复。建议:
- 删除前进行二次确认,避免误删
- 重要文件在配置中设置
allow-delete: false禁止删除 - 考虑实现软删除机制(标记删除而不是物理删除)
- 定期备份重要文件到其他存储位置
- 使用对象存储的版本控制功能(如果支持)
Q: 如何禁止删除某些文件?
A: 在配置文件中设置:
yaml
files:
- business-type: important
allow-delete: false # 禁止删除1
2
3
2
3
Q: 批量删除时如何提高效率?
A: 使用批量删除接口,服务端已优化并发处理:
javascript
// ✅ 推荐:使用批量删除接口
async function batchDelete(businessType, filePaths) {
const response = await fetch(`/delete/batch/${businessType}`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ filePaths })
});
const result = await response.json();
if (result.successful) {
const data = result.data;
console.log(`删除完成: 成功${data.successCount}/${data.totalCount}`);
// 处理失败的文件
if (data.failedCount > 0) {
const failed = data.results.filter(r => !r.success);
console.error('删除失败:', failed);
}
return data;
}
throw new Error(result.msg);
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
优化建议:
- 使用批量删除接口
/delete/batch,服务端已处理并发控制(默认并发数5) - 单次请求最多100个文件,超过需要分批处理
- 批量删除接口返回每个文件的详细结果,便于处理失败情况
- 单个文件失败不影响其他文件删除
- 服务端自动进行路径安全验证和清理
Q: 如何记录删除操作日志?
A: 服务端会自动记录删除日志。客户端可以在删除前后添加业务日志:
javascript
async function deleteWithLog(businessType, filePath, operator) {
console.log(`[${new Date().toISOString()}] ${operator} 删除文件: ${filePath}`);
const result = await deleteFile(businessType, filePath);
console.log(`[${new Date().toISOString()}] 删除结果:`, result);
return result;
}1
2
3
4
5
6
2
3
4
5
6
Q: 删除失败如何处理?
A: 根据错误信息进行不同处理:
javascript
async function handleDelete(businessType, filePath) {
try {
const result = await deleteFile(businessType, filePath);
if (!result.successful) {
// 根据不同的错误信息进行相应处理
if (result.msg.includes('不允许删除')) {
alert('该文件受保护,不允许删除');
// 可以禁用删除按钮
} else if (result.msg.includes('不存在')) {
alert('文件不存在或已被删除');
// 刷新文件列表,移除已删除的文件
refreshFileList();
} else if (result.msg.includes('非法的文件路径')) {
alert('文件路径不合法,请检查路径格式');
} else if (result.msg.includes('业务类型不存在')) {
alert('业务类型配置错误,请联系管理员');
} else {
// 其他错误,可以重试
const retry = confirm('删除失败,是否重试?');
if (retry) {
return await handleDelete(businessType, filePath);
}
}
return false;
}
return true;
} catch (error) {
console.error('删除请求失败:', error);
alert('网络错误,请检查网络连接后重试');
return false;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
处理策略:
- 权限错误:提示用户,禁用删除功能
- 文件不存在:刷新列表,移除已删除项
- 路径错误:提示用户检查路径
- 网络错误:提供重试选项
- 其他错误:记录日志,联系管理员
Q: 路径中包含特殊字符如何处理?
A: 使用URL编码或查询参数方式:
javascript
// 方式1:使用URL编码(路径参数方式)
const encodedPath = encodeURIComponent(filePath).replace(/%2F/g, '/');
await fetch(`/delete/${businessType}/${encodedPath}`, { method: 'POST' });
// 方式2:使用查询参数(推荐,自动处理编码)
const params = new URLSearchParams({ filePath });
await fetch(`/delete/${businessType}?${params}`, { method: 'POST' });1
2
3
4
5
6
7
2
3
4
5
6
7
服务端会自动清理路径,去除多余的斜杠和危险字符。
Q: 如何实现软删除功能?
A: 可以在客户端或服务端实现软删除:
javascript
// 客户端实现:先标记,再删除
async function softDelete(businessType, filePath) {
// 1. 标记为已删除(保存到本地存储或数据库)
await markAsDeleted(businessType, filePath);
// 2. 执行实际删除
const result = await deleteFile(businessType, filePath);
if (result.successful) {
// 3. 更新标记状态
await updateDeleteStatus(businessType, filePath, 'deleted');
} else {
// 4. 删除失败,恢复标记
await updateDeleteStatus(businessType, filePath, 'active');
}
return result;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
或者修改服务端逻辑,先移动到回收站目录,再定期清理。