@kne/fastify-file-manager 1.1.1 → 1.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +5 -3
- package/libs/controllers/index.js +84 -21
- package/libs/services/file-record.js +72 -20
- package/package.json +2 -1
package/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fp = require('fastify-plugin');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const fs = require('fs-extra');
|
|
4
|
+
const packageJson = require('./package.json');
|
|
4
5
|
|
|
5
6
|
module.exports = fp(
|
|
6
7
|
async (fastify, options) => {
|
|
@@ -8,12 +9,13 @@ module.exports = fp(
|
|
|
8
9
|
{
|
|
9
10
|
root: path.join(process.cwd(), 'static'),
|
|
10
11
|
namespace: 'default',
|
|
11
|
-
prefix: '/
|
|
12
|
+
prefix: `/api/v${packageJson.version.split('.')[0]}/static`,
|
|
13
|
+
dbTableNamePrefix: 't_file_manager_',
|
|
12
14
|
multipart: {},
|
|
13
15
|
static: {},
|
|
14
16
|
authenticateFileRead: async () => {},
|
|
15
17
|
authenticateFileMange: async () => {},
|
|
16
|
-
|
|
18
|
+
authenticateFileWrite: async () => {}
|
|
17
19
|
},
|
|
18
20
|
options
|
|
19
21
|
);
|
|
@@ -26,7 +28,7 @@ module.exports = fp(
|
|
|
26
28
|
[
|
|
27
29
|
'models',
|
|
28
30
|
await fastify.sequelize.addModels(path.resolve(__dirname, './libs/models'), {
|
|
29
|
-
prefix:
|
|
31
|
+
prefix: options.dbTableNamePrefix
|
|
30
32
|
})
|
|
31
33
|
],
|
|
32
34
|
['services', path.resolve(__dirname, './libs/services')],
|
|
@@ -5,7 +5,15 @@ module.exports = fp(async (fastify, options) => {
|
|
|
5
5
|
fastify.post(
|
|
6
6
|
`${options.prefix}/upload`,
|
|
7
7
|
{
|
|
8
|
-
onRequest: [options.
|
|
8
|
+
onRequest: [options.authenticateFileWrite],
|
|
9
|
+
schema: {
|
|
10
|
+
query: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
namespace: { type: 'string' }
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
9
17
|
},
|
|
10
18
|
async request => {
|
|
11
19
|
const file = await request.file();
|
|
@@ -13,7 +21,10 @@ module.exports = fp(async (fastify, options) => {
|
|
|
13
21
|
throw new Error('不能获取到上传文件');
|
|
14
22
|
}
|
|
15
23
|
//1. 保存到服务器目录 2.对接oss
|
|
16
|
-
return await services.fileRecord.uploadToFileSystem({
|
|
24
|
+
return await services.fileRecord.uploadToFileSystem({
|
|
25
|
+
file,
|
|
26
|
+
namespace: request.query.namespace || options.namespace
|
|
27
|
+
});
|
|
17
28
|
}
|
|
18
29
|
);
|
|
19
30
|
|
|
@@ -33,7 +44,7 @@ module.exports = fp(async (fastify, options) => {
|
|
|
33
44
|
},
|
|
34
45
|
async request => {
|
|
35
46
|
const { id } = request.params;
|
|
36
|
-
return await services.fileRecord.getFileUrl({ id
|
|
47
|
+
return await services.fileRecord.getFileUrl({ id });
|
|
37
48
|
}
|
|
38
49
|
);
|
|
39
50
|
|
|
@@ -62,57 +73,109 @@ module.exports = fp(async (fastify, options) => {
|
|
|
62
73
|
const { id } = request.params;
|
|
63
74
|
const { attachment, filename: targetFilename } = request.query;
|
|
64
75
|
const { targetFileName, filename } = await services.fileRecord.getFileInfo({
|
|
65
|
-
id
|
|
66
|
-
namespace: options.namespace
|
|
76
|
+
id
|
|
67
77
|
});
|
|
68
78
|
return attachment ? reply.download(targetFileName, targetFilename || filename) : reply.sendFile(targetFileName);
|
|
69
79
|
}
|
|
70
80
|
);
|
|
71
81
|
|
|
72
|
-
fastify.
|
|
82
|
+
fastify.post(
|
|
73
83
|
`${options.prefix}/file-list`,
|
|
74
84
|
{
|
|
75
85
|
onRequest: [options.authenticateFileMange],
|
|
76
86
|
schema: {
|
|
77
|
-
|
|
87
|
+
body: {
|
|
88
|
+
type: 'object',
|
|
89
|
+
properties: {
|
|
90
|
+
perPage: { type: 'number' },
|
|
91
|
+
currentPage: { type: 'number' },
|
|
92
|
+
filter: {
|
|
93
|
+
type: 'object',
|
|
94
|
+
properties: {
|
|
95
|
+
namespace: { type: 'string' },
|
|
96
|
+
size: { type: 'array', items: { type: 'number' } },
|
|
97
|
+
filename: { type: 'string' }
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
78
102
|
}
|
|
79
103
|
},
|
|
80
104
|
async request => {
|
|
81
|
-
const { filter, perPage, currentPage } = Object.assign(
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
105
|
+
const { filter, perPage, currentPage } = Object.assign(
|
|
106
|
+
{},
|
|
107
|
+
{
|
|
108
|
+
perPage: 20,
|
|
109
|
+
currentPage: 1
|
|
110
|
+
},
|
|
111
|
+
request.body
|
|
112
|
+
);
|
|
85
113
|
return await services.fileRecord.getFileList({
|
|
86
114
|
filter,
|
|
87
|
-
namespace: options.namespace,
|
|
88
115
|
perPage,
|
|
89
116
|
currentPage
|
|
90
117
|
});
|
|
91
118
|
}
|
|
92
119
|
);
|
|
93
120
|
|
|
121
|
+
// Replace file
|
|
122
|
+
|
|
123
|
+
fastify.post(
|
|
124
|
+
`${options.prefix}/replace-file`,
|
|
125
|
+
{
|
|
126
|
+
onRequest: [options.authenticateFileMange],
|
|
127
|
+
schema: {
|
|
128
|
+
type: 'object',
|
|
129
|
+
properties: {
|
|
130
|
+
id: { type: 'string' }
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
async request => {
|
|
135
|
+
const file = await request.file();
|
|
136
|
+
if (!file) {
|
|
137
|
+
throw new Error('不能获取到上传文件');
|
|
138
|
+
}
|
|
139
|
+
return await services.fileRecord.uploadToFileSystem({ id: request.query.id, file });
|
|
140
|
+
}
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
fastify.post(
|
|
144
|
+
`${options.prefix}/rename-file`,
|
|
145
|
+
{
|
|
146
|
+
onRequest: [options.authenticateFileMange],
|
|
147
|
+
schema: {
|
|
148
|
+
type: 'object',
|
|
149
|
+
properties: {
|
|
150
|
+
id: { type: 'string' },
|
|
151
|
+
filename: { type: 'string' }
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
async request => {
|
|
156
|
+
await services.fileRecord.renameFile(request.body);
|
|
157
|
+
return {};
|
|
158
|
+
}
|
|
159
|
+
);
|
|
160
|
+
|
|
94
161
|
fastify.post(
|
|
95
|
-
`${options.prefix}/delete-
|
|
162
|
+
`${options.prefix}/delete-files`,
|
|
96
163
|
{
|
|
97
164
|
onRequest: [options.authenticateFileMange],
|
|
98
165
|
schema: {
|
|
99
166
|
body: {
|
|
100
167
|
type: 'object',
|
|
101
|
-
required: ['
|
|
168
|
+
required: ['ids'],
|
|
102
169
|
properties: {
|
|
103
|
-
|
|
170
|
+
ids: { type: 'array', items: { type: 'string' } }
|
|
104
171
|
}
|
|
105
172
|
}
|
|
106
173
|
}
|
|
107
174
|
},
|
|
108
175
|
async request => {
|
|
109
|
-
const {
|
|
110
|
-
await services.fileRecord.
|
|
176
|
+
const { ids } = request.body;
|
|
177
|
+
await services.fileRecord.deleteFiles({ ids });
|
|
111
178
|
return {};
|
|
112
179
|
}
|
|
113
180
|
);
|
|
114
|
-
|
|
115
|
-
fastify.get(`${options.prefix}`, async () => {
|
|
116
|
-
return 'living';
|
|
117
|
-
});
|
|
118
181
|
});
|
|
@@ -5,7 +5,8 @@ const path = require('path');
|
|
|
5
5
|
|
|
6
6
|
module.exports = fp(async (fastify, options) => {
|
|
7
7
|
const { models, services } = fastify.fileManager;
|
|
8
|
-
const
|
|
8
|
+
const { Op } = fastify.sequelize.Sequelize;
|
|
9
|
+
const uploadToFileSystem = async ({ id, file, namespace }) => {
|
|
9
10
|
const { filename, encoding, mimetype } = file;
|
|
10
11
|
const buffer = await file.toBuffer();
|
|
11
12
|
const hash = crypto.createHash('md5');
|
|
@@ -14,20 +15,38 @@ module.exports = fp(async (fastify, options) => {
|
|
|
14
15
|
const extension = path.extname(filename);
|
|
15
16
|
const filepath = path.resolve(options.root, `${digest}${extension}`);
|
|
16
17
|
await fs.writeFile(filepath, buffer);
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
18
|
+
|
|
19
|
+
const outputFile = await (async create => {
|
|
20
|
+
if (!id) {
|
|
21
|
+
return await create();
|
|
22
|
+
}
|
|
23
|
+
const file = await models.fileRecord.findOne({ where: { uuid: id } });
|
|
24
|
+
if (!file) {
|
|
25
|
+
throw new Error('原文件不存在');
|
|
26
|
+
}
|
|
27
|
+
file.filename = filename;
|
|
28
|
+
file.encoding = encoding;
|
|
29
|
+
file.mimetype = mimetype;
|
|
30
|
+
file.hash = digest;
|
|
31
|
+
file.size = buffer.byteLength;
|
|
32
|
+
await file.save();
|
|
33
|
+
return file;
|
|
34
|
+
})(() =>
|
|
35
|
+
models.fileRecord.create({
|
|
36
|
+
filename,
|
|
37
|
+
namespace: namespace || options.namespace,
|
|
38
|
+
encoding,
|
|
39
|
+
mimetype,
|
|
40
|
+
hash: digest,
|
|
41
|
+
size: buffer.byteLength
|
|
42
|
+
})
|
|
43
|
+
);
|
|
25
44
|
return Object.assign({}, outputFile.get({ plain: true }), { id: outputFile.uuid });
|
|
26
45
|
};
|
|
27
46
|
|
|
28
47
|
const getFileUrl = async ({ id, namespace }) => {
|
|
29
48
|
const file = await models.fileRecord.findOne({
|
|
30
|
-
where: { uuid: id
|
|
49
|
+
where: { uuid: id }
|
|
31
50
|
});
|
|
32
51
|
if (!file) {
|
|
33
52
|
throw new Error('文件不存在');
|
|
@@ -36,9 +55,9 @@ module.exports = fp(async (fastify, options) => {
|
|
|
36
55
|
return `${options.prefix}/file/${file.hash}${extension}?filename=${file.filename}`;
|
|
37
56
|
};
|
|
38
57
|
|
|
39
|
-
const getFileInfo = async ({ id
|
|
58
|
+
const getFileInfo = async ({ id }) => {
|
|
40
59
|
const file = await models.fileRecord.findOne({
|
|
41
|
-
where: { uuid: id
|
|
60
|
+
where: { uuid: id }
|
|
42
61
|
});
|
|
43
62
|
if (!file) {
|
|
44
63
|
throw new Error('文件不存在');
|
|
@@ -50,11 +69,33 @@ module.exports = fp(async (fastify, options) => {
|
|
|
50
69
|
});
|
|
51
70
|
};
|
|
52
71
|
|
|
53
|
-
const getFileList = async ({ filter,
|
|
54
|
-
|
|
72
|
+
const getFileList = async ({ filter, currentPage, perPage }) => {
|
|
73
|
+
// namespace: namespace || options.namespace
|
|
74
|
+
const queryFilter = {};
|
|
75
|
+
|
|
76
|
+
if (filter?.filename) {
|
|
77
|
+
queryFilter.filename = {
|
|
78
|
+
[Op.like]: `%${filter.filename}%`
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
if (filter?.size && filter.size.length > 0) {
|
|
82
|
+
queryFilter.size = {};
|
|
83
|
+
if (filter.size[0]) {
|
|
84
|
+
queryFilter.size[Op.gt] = filter.size[0] * 1024;
|
|
85
|
+
}
|
|
86
|
+
if (filter.size[1]) {
|
|
87
|
+
queryFilter.size[Op.lt] = filter.size[1] * 1024;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (filter?.namespace) {
|
|
91
|
+
queryFilter.namespace = {
|
|
92
|
+
[Op.like]: `%${filter.namespace}%`
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
55
96
|
const { count, rows } = await models.fileRecord.findAndCountAll({
|
|
56
97
|
where: queryFilter,
|
|
57
|
-
offset:
|
|
98
|
+
offset: perPage * (currentPage - 1),
|
|
58
99
|
limit: perPage
|
|
59
100
|
});
|
|
60
101
|
return {
|
|
@@ -63,15 +104,26 @@ module.exports = fp(async (fastify, options) => {
|
|
|
63
104
|
};
|
|
64
105
|
};
|
|
65
106
|
|
|
66
|
-
const
|
|
107
|
+
const deleteFiles = async ({ ids }) => {
|
|
108
|
+
await models.fileRecord.destroy({
|
|
109
|
+
where: {
|
|
110
|
+
uuid: {
|
|
111
|
+
[Op.in]: ids
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
const renameFile = async ({ id, filename }) => {
|
|
67
118
|
const file = await models.fileRecord.findOne({
|
|
68
|
-
where: { uuid: id
|
|
119
|
+
where: { uuid: id }
|
|
69
120
|
});
|
|
70
121
|
if (!file) {
|
|
71
122
|
throw new Error('文件不存在');
|
|
72
123
|
}
|
|
73
|
-
|
|
74
|
-
await file.
|
|
124
|
+
file.filename = filename;
|
|
125
|
+
await file.save();
|
|
75
126
|
};
|
|
76
|
-
|
|
127
|
+
|
|
128
|
+
services.fileRecord = { uploadToFileSystem, getFileUrl, getFileInfo, getFileList, deleteFiles, renameFile };
|
|
77
129
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kne/fastify-file-manager",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.3",
|
|
4
4
|
"description": "用于管理静态文件上传查看等",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
"fastify": "^4.27.0",
|
|
37
37
|
"husky": "^9.0.11",
|
|
38
38
|
"prettier": "^3.2.5",
|
|
39
|
+
"qs": "^6.12.3",
|
|
39
40
|
"sqlite3": "^5.1.7"
|
|
40
41
|
},
|
|
41
42
|
"dependencies": {
|