@kne/fastify-file-manager 2.0.14 → 3.0.0-alpha.1
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.
|
@@ -27,13 +27,21 @@ module.exports = ({ DataTypes }) => {
|
|
|
27
27
|
type: DataTypes.STRING,
|
|
28
28
|
allowNull: false,
|
|
29
29
|
comment: '存储类型:local本地文件系统,oss远程oss存储'
|
|
30
|
+
},
|
|
31
|
+
options: {
|
|
32
|
+
type: DataTypes.JSONB,
|
|
33
|
+
defaultValue: {},
|
|
34
|
+
comment: '扩展字段'
|
|
30
35
|
}
|
|
31
36
|
},
|
|
32
37
|
options: {
|
|
33
38
|
indexes: [
|
|
34
39
|
{
|
|
35
40
|
unique: true,
|
|
36
|
-
fields: ['uuid',
|
|
41
|
+
fields: ['uuid'],
|
|
42
|
+
where: {
|
|
43
|
+
deleted_at: null
|
|
44
|
+
}
|
|
37
45
|
},
|
|
38
46
|
{
|
|
39
47
|
fields: ['namespace']
|
|
@@ -6,12 +6,14 @@ const { NotFound } = require('http-errors');
|
|
|
6
6
|
const os = require('node:os');
|
|
7
7
|
const { Readable } = require('node:stream');
|
|
8
8
|
const compressing = require('compressing');
|
|
9
|
+
const { glob } = require('glob');
|
|
10
|
+
const MimeTypes = require('mime-types');
|
|
9
11
|
|
|
10
|
-
module.exports = fp(async (fastify,
|
|
12
|
+
module.exports = fp(async (fastify, fastifyOptions) => {
|
|
11
13
|
const { models, services } = fastify.fileManager;
|
|
12
14
|
const { Op } = fastify.sequelize.Sequelize;
|
|
13
15
|
|
|
14
|
-
const detail = async ({ id, uuid }) => {
|
|
16
|
+
const detail = async ({ id, uuid, namespace }) => {
|
|
15
17
|
const file = await models.fileRecord.findOne({
|
|
16
18
|
where: { uuid: String(id || uuid).split('?')[0] }
|
|
17
19
|
});
|
|
@@ -22,7 +24,7 @@ module.exports = fp(async (fastify, options) => {
|
|
|
22
24
|
|
|
23
25
|
return file;
|
|
24
26
|
};
|
|
25
|
-
const uploadToFileSystem = async ({ id, file, namespace }) => {
|
|
27
|
+
const uploadToFileSystem = async ({ id, file, namespace, options }) => {
|
|
26
28
|
const { filename, encoding, mimetype } = file;
|
|
27
29
|
const hash = crypto.createHash('md5');
|
|
28
30
|
const extension = path.extname(filename);
|
|
@@ -63,7 +65,7 @@ module.exports = fp(async (fastify, options) => {
|
|
|
63
65
|
const digest = hash.digest('hex');
|
|
64
66
|
|
|
65
67
|
let storageType;
|
|
66
|
-
const ossServices =
|
|
68
|
+
const ossServices = fastifyOptions.ossAdapter();
|
|
67
69
|
if (typeof ossServices.uploadFile === 'function') {
|
|
68
70
|
// 使用流上传到OSS
|
|
69
71
|
const readStream = fs.createReadStream(tmpPath);
|
|
@@ -71,7 +73,7 @@ module.exports = fp(async (fastify, options) => {
|
|
|
71
73
|
storageType = 'oss';
|
|
72
74
|
} else {
|
|
73
75
|
// 使用流写入本地文件
|
|
74
|
-
const filepath = path.resolve(
|
|
76
|
+
const filepath = path.resolve(fastifyOptions.root, `${digest}${extension}`);
|
|
75
77
|
const writeStream = fs.createWriteStream(filepath);
|
|
76
78
|
const readStream = fs.createReadStream(tmpPath);
|
|
77
79
|
await new Promise((resolve, reject) => {
|
|
@@ -94,23 +96,25 @@ module.exports = fp(async (fastify, options) => {
|
|
|
94
96
|
file.hash = digest;
|
|
95
97
|
file.size = fileSize;
|
|
96
98
|
file.storageType = storageType;
|
|
99
|
+
file.options = options;
|
|
97
100
|
await file.save();
|
|
98
101
|
return file;
|
|
99
102
|
})(() =>
|
|
100
103
|
models.fileRecord.create({
|
|
101
104
|
filename,
|
|
102
|
-
namespace: namespace ||
|
|
105
|
+
namespace: namespace || fastifyOptions.namespace,
|
|
103
106
|
encoding,
|
|
104
107
|
mimetype,
|
|
105
108
|
hash: digest,
|
|
106
109
|
size: fileSize,
|
|
107
|
-
storageType
|
|
110
|
+
storageType,
|
|
111
|
+
options
|
|
108
112
|
})
|
|
109
113
|
);
|
|
110
114
|
return Object.assign({}, outputFile.get({ plain: true }), { id: outputFile.uuid });
|
|
111
115
|
};
|
|
112
116
|
|
|
113
|
-
const uploadFromUrl = async ({ id, url, filename: originFilename, namespace }) => {
|
|
117
|
+
const uploadFromUrl = async ({ id, url, filename: originFilename, namespace, options }) => {
|
|
114
118
|
const response = await fetch(url);
|
|
115
119
|
if (!response.ok) {
|
|
116
120
|
throw new Error('下载文件失败');
|
|
@@ -166,13 +170,13 @@ module.exports = fp(async (fastify, options) => {
|
|
|
166
170
|
encoding: 'binary',
|
|
167
171
|
file: nodeStream
|
|
168
172
|
};
|
|
169
|
-
return await uploadToFileSystem({ id, file: tempFile, namespace });
|
|
173
|
+
return await uploadToFileSystem({ id, file: tempFile, namespace, options });
|
|
170
174
|
};
|
|
171
175
|
|
|
172
176
|
const getFileUrl = async ({ id, namespace }) => {
|
|
173
|
-
const file = await detail({ id });
|
|
177
|
+
const file = await detail({ id, namespace });
|
|
174
178
|
const extension = path.extname(file.filename);
|
|
175
|
-
const ossServices =
|
|
179
|
+
const ossServices = fastifyOptions.ossAdapter();
|
|
176
180
|
if (file.storageType === 'oss' && typeof ossServices.getFileLink !== 'function') {
|
|
177
181
|
throw new Error('ossAdapter未正确配置无法读取oss类型存储文件');
|
|
178
182
|
}
|
|
@@ -180,17 +184,17 @@ module.exports = fp(async (fastify, options) => {
|
|
|
180
184
|
return await ossServices.getFileLink({ filename: `${file.hash}${extension}` });
|
|
181
185
|
}
|
|
182
186
|
|
|
183
|
-
if (!(await fs.exists(`${
|
|
187
|
+
if (!(await fs.exists(`${fastifyOptions.root}/${file.hash}${extension}`))) {
|
|
184
188
|
throw new NotFound();
|
|
185
189
|
}
|
|
186
|
-
return `${
|
|
190
|
+
return `${fastifyOptions.prefix}/file/${file.hash}${extension}?filename=${file.filename}`;
|
|
187
191
|
};
|
|
188
192
|
|
|
189
|
-
const getFileInfo = async ({ id }) => {
|
|
190
|
-
const file = await detail({ id });
|
|
193
|
+
const getFileInfo = async ({ id, namespace }) => {
|
|
194
|
+
const file = await detail({ id, namespace });
|
|
191
195
|
const extension = path.extname(file.filename);
|
|
192
196
|
const targetFileName = `${file.hash}${extension}`;
|
|
193
|
-
const ossServices =
|
|
197
|
+
const ossServices = fastifyOptions.ossAdapter();
|
|
194
198
|
if (file.storageType === 'oss' && typeof ossServices.downloadFile !== 'function') {
|
|
195
199
|
throw new Error('ossAdapter未正确配置无法读取oss类型存储文件');
|
|
196
200
|
}
|
|
@@ -205,8 +209,7 @@ module.exports = fp(async (fastify, options) => {
|
|
|
205
209
|
});
|
|
206
210
|
};
|
|
207
211
|
|
|
208
|
-
const getFileList = async ({ filter = {}, currentPage, perPage }) => {
|
|
209
|
-
// namespace: namespace || options.namespace
|
|
212
|
+
const getFileList = async ({ filter = {}, namespace, currentPage, perPage }) => {
|
|
210
213
|
const queryFilter = {};
|
|
211
214
|
|
|
212
215
|
if (filter?.filename) {
|
|
@@ -228,6 +231,9 @@ module.exports = fp(async (fastify, options) => {
|
|
|
228
231
|
[Op.like]: `%${filter.namespace}%`
|
|
229
232
|
};
|
|
230
233
|
}
|
|
234
|
+
if (namespace) {
|
|
235
|
+
queryFilter.namespace = namespace;
|
|
236
|
+
}
|
|
231
237
|
|
|
232
238
|
if (filter?.id) {
|
|
233
239
|
queryFilter.uuid = filter.id;
|
|
@@ -281,15 +287,15 @@ module.exports = fp(async (fastify, options) => {
|
|
|
281
287
|
await file.save();
|
|
282
288
|
};
|
|
283
289
|
|
|
284
|
-
const getFileBlob = async ({ id }) => {
|
|
285
|
-
const file = await detail({ id });
|
|
290
|
+
const getFileBlob = async ({ id, namespace }) => {
|
|
291
|
+
const file = await detail({ id, namespace });
|
|
286
292
|
if (!file) {
|
|
287
293
|
throw new Error('文件不存在');
|
|
288
294
|
}
|
|
289
295
|
|
|
290
296
|
const extension = path.extname(file.filename);
|
|
291
297
|
const targetFileName = `${file.hash}${extension}`;
|
|
292
|
-
const ossServices =
|
|
298
|
+
const ossServices = fastifyOptions.ossAdapter();
|
|
293
299
|
|
|
294
300
|
let buffer;
|
|
295
301
|
if (file.storageType === 'oss') {
|
|
@@ -298,7 +304,7 @@ module.exports = fp(async (fastify, options) => {
|
|
|
298
304
|
}
|
|
299
305
|
buffer = await ossServices.downloadFile({ filename: targetFileName });
|
|
300
306
|
} else {
|
|
301
|
-
const filePath = path.resolve(
|
|
307
|
+
const filePath = path.resolve(fastifyOptions.root, targetFileName);
|
|
302
308
|
if (!(await fs.exists(filePath))) {
|
|
303
309
|
throw new NotFound();
|
|
304
310
|
}
|
|
@@ -314,14 +320,14 @@ module.exports = fp(async (fastify, options) => {
|
|
|
314
320
|
const getFileReadStream = file => {
|
|
315
321
|
const extension = path.extname(file.filename);
|
|
316
322
|
const targetFileName = `${file.hash}${extension}`;
|
|
317
|
-
const ossServices =
|
|
323
|
+
const ossServices = fastifyOptions.ossAdapter();
|
|
318
324
|
if (file.storageType === 'oss') {
|
|
319
325
|
if (typeof ossServices.getFileStream !== 'function') {
|
|
320
326
|
throw new Error('ossAdapter未正确配置无法读取oss类型存储文件');
|
|
321
327
|
}
|
|
322
328
|
return ossServices.getFileStream({ filename: targetFileName });
|
|
323
329
|
} else {
|
|
324
|
-
const filePath = path.resolve(
|
|
330
|
+
const filePath = path.resolve(fastifyOptions.root, targetFileName);
|
|
325
331
|
return fs.createReadStream(filePath);
|
|
326
332
|
}
|
|
327
333
|
};
|
|
@@ -376,6 +382,46 @@ module.exports = fp(async (fastify, options) => {
|
|
|
376
382
|
});
|
|
377
383
|
};
|
|
378
384
|
|
|
385
|
+
//文件解压缩
|
|
386
|
+
const uncompressFile = async ({ id, type = 'zip', namespace, globOptions = '**/*' }) => {
|
|
387
|
+
const file = await detail({ id });
|
|
388
|
+
const fileStream = await getFileReadStream(file);
|
|
389
|
+
const tmpPath = path.resolve(os.tmpdir(), `temp_${id}_${crypto.randomBytes(6).toString('hex')}`);
|
|
390
|
+
await compressing[type].uncompress(fileStream, tmpPath);
|
|
391
|
+
const files = await glob(globOptions, {
|
|
392
|
+
cwd: tmpPath,
|
|
393
|
+
nodir: true
|
|
394
|
+
});
|
|
395
|
+
//将文件上传到文件系统
|
|
396
|
+
const fileList = await Promise.all(
|
|
397
|
+
files.map(async dir => {
|
|
398
|
+
const filepath = path.resolve(tmpPath, dir);
|
|
399
|
+
const filename = path.basename(dir);
|
|
400
|
+
const fileStream = fs.createReadStream(filepath);
|
|
401
|
+
|
|
402
|
+
const mimetype = MimeTypes.lookup(filepath) || 'application/octet-stream';
|
|
403
|
+
const file = await uploadToFileSystem({
|
|
404
|
+
file: {
|
|
405
|
+
filename,
|
|
406
|
+
mimetype,
|
|
407
|
+
encoding: 'binary',
|
|
408
|
+
file: fileStream
|
|
409
|
+
},
|
|
410
|
+
filename,
|
|
411
|
+
namespace
|
|
412
|
+
});
|
|
413
|
+
return {
|
|
414
|
+
dir,
|
|
415
|
+
file
|
|
416
|
+
};
|
|
417
|
+
})
|
|
418
|
+
);
|
|
419
|
+
//清除临时文件
|
|
420
|
+
await fs.remove(tmpPath);
|
|
421
|
+
|
|
422
|
+
return fileList;
|
|
423
|
+
};
|
|
424
|
+
|
|
379
425
|
const getFileInstance = async ({ id, uuid }) => {
|
|
380
426
|
return detail({ id, uuid });
|
|
381
427
|
};
|
|
@@ -393,6 +439,7 @@ module.exports = fp(async (fastify, options) => {
|
|
|
393
439
|
getFileReadStream,
|
|
394
440
|
getCompressFileStream,
|
|
395
441
|
getCompressFileBlob,
|
|
442
|
+
uncompressFile,
|
|
396
443
|
getFileInstance, // 兼容之前api,后面可能会删掉
|
|
397
444
|
fileRecord: {
|
|
398
445
|
uploadToFileSystem,
|
|
@@ -407,6 +454,7 @@ module.exports = fp(async (fastify, options) => {
|
|
|
407
454
|
getFileReadStream,
|
|
408
455
|
getCompressFileStream,
|
|
409
456
|
getCompressFileBlob,
|
|
457
|
+
uncompressFile,
|
|
410
458
|
getFileInstance
|
|
411
459
|
}
|
|
412
460
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kne/fastify-file-manager",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.0-alpha.1",
|
|
4
4
|
"description": "用于管理静态文件上传查看等",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -51,6 +51,8 @@
|
|
|
51
51
|
"@fastify/static": "^8.1.1",
|
|
52
52
|
"compressing": "^2.0.0",
|
|
53
53
|
"fs-extra": "^11.2.0",
|
|
54
|
-
"
|
|
54
|
+
"glob": "^13.0.0",
|
|
55
|
+
"http-errors": "^2.0.0",
|
|
56
|
+
"mime-types": "^3.0.2"
|
|
55
57
|
}
|
|
56
58
|
}
|