@kne/fastify-file-manager 2.0.0-alpha.0 → 2.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.
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const fp = require('fastify-plugin');
2
- const path = require('path');
2
+ const path = require('node:path');
3
3
  const fs = require('fs-extra');
4
4
  const packageJson = require('./package.json');
5
5
 
@@ -31,6 +31,7 @@ module.exports = fp(
31
31
  fastify.register(require('@kne/fastify-namespace'), {
32
32
  name: 'fileManager',
33
33
  options,
34
+ singleton: true,
34
35
  modules: [
35
36
  [
36
37
  'models',
@@ -1,134 +1,148 @@
1
1
  const fp = require('fastify-plugin');
2
2
 
3
3
  module.exports = fp(async (fastify, options) => {
4
- const {services} = fastify.fileManager;
5
- fastify.post(`${options.prefix}/upload`, {
6
- onRequest: options.createAuthenticate('file:write'), schema: {
7
- summary: '上传文件', description: '上传单个文件到服务器或配置的存储服务', query: {
8
- type: 'object', properties: {
9
- namespace: {type: 'string', description: '文件分类命名空间'}
10
- }
11
- }
12
- }
13
- }, async request => {
14
- const file = await request.file();
15
- if (!file) {
16
- throw new Error('不能获取到上传文件');
4
+ const { services } = fastify.fileManager;
5
+ fastify.post(`${options.prefix}/upload`, {
6
+ onRequest: options.createAuthenticate('file:write'), schema: {
7
+ summary: '上传文件', description: '上传单个文件到服务器或配置的存储服务', query: {
8
+ type: 'object', properties: {
9
+ namespace: { type: 'string', description: '文件分类命名空间' }
17
10
  }
18
- //1. 保存到服务器目录 2.对接oss
19
- return await services.fileRecord.uploadToFileSystem({
20
- file, namespace: request.query.namespace || options.namespace
21
- });
11
+ }
12
+ }
13
+ }, async request => {
14
+ const file = await request.file();
15
+ if (!file) {
16
+ throw new Error('不能获取到上传文件');
17
+ }
18
+ return await services.fileRecord.uploadToFileSystem({
19
+ file, namespace: request.query.namespace || options.namespace
22
20
  });
21
+ });
23
22
 
24
- fastify.get(`${options.prefix}/file-url/:id`, {
25
- onRequest: options.createAuthenticate('file:read'), schema: {
26
- summary: '获取文件url', description: '获取文件url', params: {
27
- type: 'object', required: ['id'], properties: {
28
- id: {type: 'string', description: '文件id'}
29
- }
30
- }
23
+ fastify.post(`${options.prefix}/uploadUrl`, {
24
+ onRequest: options.createAuthenticate('file:write'), schema: {
25
+ summary: '上传URL文件', description: '上传单个文件到服务器或配置的存储服务', body: {
26
+ type: 'object', properties: {
27
+ url: { type: 'string', description: '文件url' },
28
+ namespace: { type: 'string', description: '文件分类命名空间' }
31
29
  }
32
- }, async request => {
33
- const {id} = request.params;
34
- return await services.fileRecord.getFileUrl({id});
30
+ }
31
+ }
32
+ }, async request => {
33
+ return await services.fileRecord.uploadFromUrl({
34
+ url: request.body.url, namespace: request.body.namespace || options.namespace
35
35
  });
36
+ });
36
37
 
37
- fastify.get(`${options.prefix}/file-id/:id`, {
38
- onRequest: options.createAuthenticate('file:read'), schema: {
39
- summary: '获取文件信息', description: '获取文件信息', query: {
40
- type: 'object', properties: {
41
- attachment: {type: 'boolean', description: '是否下载'},
42
- filename: {type: 'string', description: '下载文件名'}
43
- }
44
- }, params: {
45
- type: 'object', required: ['id'], properties: {
46
- id: {type: 'string', description: '文件id'}
47
- }
48
- }
38
+ fastify.get(`${options.prefix}/file-url/:id`, {
39
+ onRequest: options.createAuthenticate('file:read'), schema: {
40
+ summary: '获取文件url', description: '获取文件url', params: {
41
+ type: 'object', required: ['id'], properties: {
42
+ id: { type: 'string', description: '文件id' }
49
43
  }
50
- }, async (request, reply) => {
51
- const {id} = request.params;
52
- const {attachment, filename: targetFilename} = request.query;
53
- const {filePath, targetFile, filename, mimetype, ...props} = await services.fileRecord.getFileInfo({
54
- id
55
- });
56
- if (targetFile) {
57
- const outputFilename = encodeURIComponent(targetFilename || filename);
58
- reply.header('Content-Type', mimetype);
59
- reply.header('Content-Disposition', attachment ? `attachment; filename="${outputFilename}"` : `filename="${outputFilename}"`);
60
- return reply.send(targetFile);
44
+ }
45
+ }
46
+ }, async request => {
47
+ const { id } = request.params;
48
+ return await services.fileRecord.getFileUrl({ id });
49
+ });
50
+
51
+ fastify.get(`${options.prefix}/file-id/:id`, {
52
+ onRequest: options.createAuthenticate('file:read'), schema: {
53
+ summary: '获取文件信息', description: '获取文件信息', query: {
54
+ type: 'object', properties: {
55
+ attachment: { type: 'boolean', description: '是否下载' },
56
+ filename: { type: 'string', description: '下载文件名' }
57
+ }
58
+ }, params: {
59
+ type: 'object', required: ['id'], properties: {
60
+ id: { type: 'string', description: '文件id' }
61
61
  }
62
- return attachment ? reply.download(filePath, targetFilename || filename) : reply.sendFile(filePath);
62
+ }
63
+ }
64
+ }, async (request, reply) => {
65
+ const { id } = request.params;
66
+ const { attachment, filename: targetFilename } = request.query;
67
+ const { filePath, targetFile, filename, mimetype, ...props } = await services.fileRecord.getFileInfo({
68
+ id
63
69
  });
70
+ if (targetFile) {
71
+ const outputFilename = encodeURIComponent(targetFilename || filename);
72
+ reply.header('Content-Type', mimetype);
73
+ reply.header('Content-Disposition', attachment ? `attachment; filename="${outputFilename}"` : `filename="${outputFilename}"`);
74
+ return reply.send(targetFile);
75
+ }
76
+ return attachment ? reply.download(filePath, targetFilename || filename) : reply.sendFile(filePath);
77
+ });
64
78
 
65
- fastify.post(`${options.prefix}/file-list`, {
66
- onRequest: options.createAuthenticate('file:mange'), schema: {
67
- summary: '获取文件列表', description: '查询指定命名空间下的文件列表', body: {
68
- type: 'object', properties: {
69
- perPage: {type: 'number', description: '每页数量'},
70
- currentPage: {type: 'number', description: '当前页数'},
71
- filter: {
72
- type: 'object', properties: {
73
- namespace: {type: 'string', description: '文件分类命名空间'},
74
- size: {type: 'array', items: {type: 'number'}, description: '文件大小'},
75
- filename: {type: 'string', description: '文件名'}
76
- }
77
- }
78
- }
79
+ fastify.post(`${options.prefix}/file-list`, {
80
+ onRequest: options.createAuthenticate('file:mange'), schema: {
81
+ summary: '获取文件列表', description: '查询指定命名空间下的文件列表', body: {
82
+ type: 'object', properties: {
83
+ perPage: { type: 'number', description: '每页数量' },
84
+ currentPage: { type: 'number', description: '当前页数' },
85
+ filter: {
86
+ type: 'object', properties: {
87
+ namespace: { type: 'string', description: '文件分类命名空间' },
88
+ size: { type: 'array', items: { type: 'number' }, description: '文件大小' },
89
+ filename: { type: 'string', description: '文件名' }
79
90
  }
91
+ }
80
92
  }
81
- }, async request => {
82
- const {filter, perPage, currentPage} = Object.assign({}, {
83
- perPage: 20, currentPage: 1
84
- }, request.body);
85
- return await services.fileRecord.getFileList({
86
- filter, perPage, currentPage
87
- });
93
+ }
94
+ }
95
+ }, async request => {
96
+ const { filter, perPage, currentPage } = Object.assign({}, {
97
+ perPage: 20, currentPage: 1
98
+ }, request.body);
99
+ return await services.fileRecord.getFileList({
100
+ filter, perPage, currentPage
88
101
  });
102
+ });
89
103
 
90
- // Replace file
104
+ // Replace file
91
105
 
92
- fastify.post(`${options.prefix}/replace-file`, {
93
- onRequest: options.createAuthenticate('file:mange'), schema: {
94
- summary: '替换文件', description: '替换文件', query: {
95
- type: 'object', properties: {
96
- id: {type: 'string', description: '文件id'},
97
- }
98
- }
106
+ fastify.post(`${options.prefix}/replace-file`, {
107
+ onRequest: options.createAuthenticate('file:mange'), schema: {
108
+ summary: '替换文件', description: '替换文件', query: {
109
+ type: 'object', properties: {
110
+ id: { type: 'string', description: '文件id' }
99
111
  }
100
- }, async request => {
101
- const file = await request.file();
102
- if (!file) {
103
- throw new Error('不能获取到上传文件');
104
- }
105
- return await services.fileRecord.uploadToFileSystem({id: request.query.id, file});
106
- });
112
+ }
113
+ }
114
+ }, async request => {
115
+ const file = await request.file();
116
+ if (!file) {
117
+ throw new Error('不能获取到上传文件');
118
+ }
119
+ return await services.fileRecord.uploadToFileSystem({ id: request.query.id, file });
120
+ });
107
121
 
108
- fastify.post(`${options.prefix}/rename-file`, {
109
- onRequest: options.createAuthenticate('file:mange'), schema: {
110
- summary: '重命名文件', description: '重命名文件', body: {
111
- type: 'object', properties: {
112
- id: {type: 'string', description: '文件id'}, filename: {type: 'string', description: '新文件名'}
113
- }
114
- }
122
+ fastify.post(`${options.prefix}/rename-file`, {
123
+ onRequest: options.createAuthenticate('file:mange'), schema: {
124
+ summary: '重命名文件', description: '重命名文件', body: {
125
+ type: 'object', properties: {
126
+ id: { type: 'string', description: '文件id' }, filename: { type: 'string', description: '新文件名' }
115
127
  }
116
- }, async request => {
117
- await services.fileRecord.renameFile(request.body);
118
- return {};
119
- });
128
+ }
129
+ }
130
+ }, async request => {
131
+ await services.fileRecord.renameFile(request.body);
132
+ return {};
133
+ });
120
134
 
121
- fastify.post(`${options.prefix}/delete-files`, {
122
- onRequest: options.createAuthenticate('file:mange'), schema: {
123
- summary: '删除文件', description: '删除文件', body: {
124
- type: 'object', required: ['ids'], properties: {
125
- ids: {type: 'array', items: {type: 'string'}, description: '文件id列表'}
126
- }
127
- }
135
+ fastify.post(`${options.prefix}/delete-files`, {
136
+ onRequest: options.createAuthenticate('file:mange'), schema: {
137
+ summary: '删除文件', description: '删除文件', body: {
138
+ type: 'object', required: ['ids'], properties: {
139
+ ids: { type: 'array', items: { type: 'string' }, description: '文件id列表' }
128
140
  }
129
- }, async request => {
130
- const {ids} = request.body;
131
- await services.fileRecord.deleteFiles({ids});
132
- return {};
133
- });
141
+ }
142
+ }
143
+ }, async request => {
144
+ const { ids } = request.body;
145
+ await services.fileRecord.deleteFiles({ ids });
146
+ return {};
147
+ });
134
148
  });
@@ -2,165 +2,204 @@ const fp = require('fastify-plugin');
2
2
  const fs = require('fs-extra');
3
3
  const crypto = require('crypto');
4
4
  const path = require('path');
5
- const {NotFound} = require('http-errors');
5
+ const { NotFound } = require('http-errors');
6
6
 
7
7
  module.exports = fp(async (fastify, options) => {
8
- const {models, services} = fastify.fileManager;
9
- const {Op} = fastify.sequelize.Sequelize;
10
- const uploadToFileSystem = async ({id, file, namespace}) => {
11
- const {filename, encoding, mimetype} = file;
12
- const buffer = await file.toBuffer();
13
- const hash = crypto.createHash('md5');
14
- hash.update(buffer);
15
- const digest = hash.digest('hex');
16
- const extension = path.extname(filename);
8
+ const { models, services } = fastify.fileManager;
9
+ const { Op } = fastify.sequelize.Sequelize;
10
+ const uploadToFileSystem = async ({ id, file, namespace }) => {
11
+ const { filename, encoding, mimetype } = file;
12
+ const hash = crypto.createHash('md5');
13
+ const extension = path.extname(filename);
14
+ let buffer = Buffer.alloc(0);
17
15
 
18
- let storageType;
19
- const ossServices = options.ossAdapter();
20
- if (typeof ossServices.uploadFile === 'function') {
21
- await ossServices.uploadFile({file: buffer, filename: `${digest}${extension}`});
22
- storageType = 'oss';
23
- } else {
24
- const filepath = path.resolve(options.root, `${digest}${extension}`);
25
- await fs.writeFile(filepath, buffer);
26
- storageType = 'local';
27
- }
16
+ // 使用流处理文件数据
17
+ const stream = file.createReadStream();
18
+ for await (const chunk of stream) {
19
+ hash.update(chunk);
20
+ buffer = Buffer.concat([buffer, chunk]);
21
+ }
22
+ const digest = hash.digest('hex');
28
23
 
29
- const outputFile = await (async create => {
30
- if (!id) {
31
- return await create();
32
- }
33
- const file = await models.fileRecord.findOne({where: {uuid: id}});
34
- if (!file) {
35
- throw new Error('原文件不存在');
36
- }
37
- file.filename = filename;
38
- file.encoding = encoding;
39
- file.mimetype = mimetype;
40
- file.hash = digest;
41
- file.size = buffer.byteLength;
42
- file.storageType = storageType;
43
- await file.save();
44
- return file;
45
- })(() =>
46
- models.fileRecord.create({
47
- filename,
48
- namespace: namespace || options.namespace,
49
- encoding,
50
- mimetype,
51
- hash: digest,
52
- size: buffer.byteLength,
53
- storageType
54
- })
55
- );
56
- return Object.assign({}, outputFile.get({plain: true}), {id: outputFile.uuid});
57
- };
24
+ let storageType;
25
+ const ossServices = options.ossAdapter();
26
+ if (typeof ossServices.uploadFile === 'function') {
27
+ // 使用流上传到OSS
28
+ const uploadStream = file.createReadStream();
29
+ await ossServices.uploadFileStream({ stream: uploadStream, filename: `${digest}${extension}` });
30
+ storageType = 'oss';
31
+ } else {
32
+ // 使用流写入本地文件
33
+ const filepath = path.resolve(options.root, `${digest}${extension}`);
34
+ const writeStream = fs.createWriteStream(filepath);
35
+ const readStream = file.createReadStream();
36
+ await new Promise((resolve, reject) => {
37
+ readStream.pipe(writeStream)
38
+ .on('finish', resolve)
39
+ .on('error', reject);
40
+ });
41
+ storageType = 'local';
42
+ }
58
43
 
59
- const getFileUrl = async ({id, namespace}) => {
60
- const file = await models.fileRecord.findOne({
61
- where: {uuid: id}
62
- });
63
- if (!file) {
64
- throw new Error('文件不存在');
65
- }
66
- const extension = path.extname(file.filename);
67
- const ossServices = options.ossAdapter();
68
- if (file.storageType === 'oss' && typeof ossServices.getFileLink !== 'function') {
69
- throw new Error('ossAdapter未正确配置无法读取oss类型存储文件');
70
- }
71
- if (file.storageType === 'oss') {
72
- return await ossServices.getFileLink({filename: `${file.hash}${extension}`});
73
- }
44
+ const outputFile = await (async create => {
45
+ if (!id) {
46
+ return await create();
47
+ }
48
+ const file = await models.fileRecord.findOne({ where: { uuid: id } });
49
+ if (!file) {
50
+ throw new Error('原文件不存在');
51
+ }
52
+ file.filename = filename;
53
+ file.encoding = encoding;
54
+ file.mimetype = mimetype;
55
+ file.hash = digest;
56
+ file.size = buffer.byteLength;
57
+ file.storageType = storageType;
58
+ await file.save();
59
+ return file;
60
+ })(() =>
61
+ models.fileRecord.create({
62
+ filename,
63
+ namespace: namespace || options.namespace,
64
+ encoding,
65
+ mimetype,
66
+ hash: digest,
67
+ size: buffer.byteLength,
68
+ storageType
69
+ })
70
+ );
71
+ return Object.assign({}, outputFile.get({ plain: true }), { id: outputFile.uuid });
72
+ };
74
73
 
75
- if (!(await fs.exists(`${options.root}/${file.hash}${extension}`))) {
76
- throw new NotFound();
77
- }
78
- return `${options.prefix}/file/${file.hash}${extension}?filename=${file.filename}`;
74
+ const uploadFromUrl = async ({ id, url, namespace }) => {
75
+ const response = await fetch(url);
76
+ if (!response.ok) {
77
+ throw new Error('下载文件失败');
78
+ }
79
+ const chunks = [];
80
+ for await (const chunk of response.body) {
81
+ chunks.push(chunk);
82
+ }
83
+ const buffer = Buffer.concat(chunks);
84
+ const tempFile = {
85
+ filename: path.basename(url).split('?')[0],
86
+ mimetype: response.headers.get('content-type'),
87
+ encoding: 'binary',
88
+ createReadStream: () => {
89
+ const readable = new require('stream').Readable();
90
+ readable.push(buffer);
91
+ readable.push(null);
92
+ return readable;
93
+ }
79
94
  };
95
+ return await uploadToFileSystem({ id, file: tempFile, namespace });
96
+ };
80
97
 
81
- const getFileInfo = async ({id}) => {
82
- const file = await models.fileRecord.findOne({
83
- where: {uuid: id}
84
- });
85
- if (!file) {
86
- throw new Error('文件不存在');
87
- }
88
- const extension = path.extname(file.filename);
89
- const targetFileName = `${file.hash}${extension}`;
90
- const ossServices = options.ossAdapter();
91
- if (file.storageType === 'oss' && typeof ossServices.downloadFile !== 'function') {
92
- throw new Error('ossAdapter未正确配置无法读取oss类型存储文件');
93
- }
94
- let targetFile;
95
- if (file.storageType === 'oss') {
96
- targetFile = await ossServices.downloadFile({filename: targetFileName});
97
- }
98
- return Object.assign({}, file.get({pain: true}), {
99
- id: file.uuid,
100
- filePath: targetFileName,
101
- targetFile
102
- });
103
- };
98
+ const getFileUrl = async ({ id, namespace }) => {
99
+ const file = await models.fileRecord.findOne({
100
+ where: { uuid: id }
101
+ });
102
+ if (!file) {
103
+ throw new Error('文件不存在');
104
+ }
105
+ const extension = path.extname(file.filename);
106
+ const ossServices = options.ossAdapter();
107
+ if (file.storageType === 'oss' && typeof ossServices.getFileLink !== 'function') {
108
+ throw new Error('ossAdapter未正确配置无法读取oss类型存储文件');
109
+ }
110
+ if (file.storageType === 'oss') {
111
+ return await ossServices.getFileLink({ filename: `${file.hash}${extension}` });
112
+ }
104
113
 
105
- const getFileList = async ({filter, currentPage, perPage}) => {
106
- // namespace: namespace || options.namespace
107
- const queryFilter = {};
114
+ if (!(await fs.exists(`${options.root}/${file.hash}${extension}`))) {
115
+ throw new NotFound();
116
+ }
117
+ return `${options.prefix}/file/${file.hash}${extension}?filename=${file.filename}`;
118
+ };
108
119
 
109
- if (filter?.filename) {
110
- queryFilter.filename = {
111
- [Op.like]: `%${filter.filename}%`
112
- };
113
- }
114
- if (filter?.size && filter.size.length > 0) {
115
- queryFilter.size = {};
116
- if (filter.size[0]) {
117
- queryFilter.size[Op.gt] = filter.size[0] * 1024;
118
- }
119
- if (filter.size[1]) {
120
- queryFilter.size[Op.lt] = filter.size[1] * 1024;
121
- }
122
- }
123
- if (filter?.namespace) {
124
- queryFilter.namespace = {
125
- [Op.like]: `%${filter.namespace}%`
126
- };
127
- }
120
+ const getFileInfo = async ({ id }) => {
121
+ const file = await models.fileRecord.findOne({
122
+ where: { uuid: id }
123
+ });
124
+ if (!file) {
125
+ throw new Error('文件不存在');
126
+ }
127
+ const extension = path.extname(file.filename);
128
+ const targetFileName = `${file.hash}${extension}`;
129
+ const ossServices = options.ossAdapter();
130
+ if (file.storageType === 'oss' && typeof ossServices.downloadFile !== 'function') {
131
+ throw new Error('ossAdapter未正确配置无法读取oss类型存储文件');
132
+ }
133
+ let targetFile;
134
+ if (file.storageType === 'oss') {
135
+ targetFile = await ossServices.downloadFile({ filename: targetFileName });
136
+ }
137
+ return Object.assign({}, file.get({ pain: true }), {
138
+ id: file.uuid,
139
+ filePath: targetFileName,
140
+ targetFile
141
+ });
142
+ };
128
143
 
129
- const {count, rows} = await models.fileRecord.findAndCountAll({
130
- where: queryFilter,
131
- offset: perPage * (currentPage - 1),
132
- limit: perPage
133
- });
134
- return {
135
- pageData: rows.map(item => Object.assign({}, item.get({plain: true}), {id: item.uuid})),
136
- totalCount: count
137
- };
138
- };
144
+ const getFileList = async ({ filter, currentPage, perPage }) => {
145
+ // namespace: namespace || options.namespace
146
+ const queryFilter = {};
139
147
 
140
- const deleteFiles = async ({ids}) => {
141
- await models.fileRecord.destroy({
142
- where: {
143
- uuid: {
144
- [Op.in]: ids
145
- }
146
- }
147
- });
148
+ if (filter?.filename) {
149
+ queryFilter.filename = {
150
+ [Op.like]: `%${filter.filename}%`
151
+ };
152
+ }
153
+ if (filter?.size && filter.size.length > 0) {
154
+ queryFilter.size = {};
155
+ if (filter.size[0]) {
156
+ queryFilter.size[Op.gt] = filter.size[0] * 1024;
157
+ }
158
+ if (filter.size[1]) {
159
+ queryFilter.size[Op.lt] = filter.size[1] * 1024;
160
+ }
161
+ }
162
+ if (filter?.namespace) {
163
+ queryFilter.namespace = {
164
+ [Op.like]: `%${filter.namespace}%`
165
+ };
166
+ }
167
+
168
+ const { count, rows } = await models.fileRecord.findAndCountAll({
169
+ where: queryFilter,
170
+ offset: perPage * (currentPage - 1),
171
+ limit: perPage
172
+ });
173
+ return {
174
+ pageData: rows.map(item => Object.assign({}, item.get({ plain: true }), { id: item.uuid })),
175
+ totalCount: count
148
176
  };
177
+ };
149
178
 
150
- const renameFile = async ({id, filename}) => {
151
- const file = await models.fileRecord.findOne({
152
- where: {uuid: id}
153
- });
154
- if (!file) {
155
- throw new Error('文件不存在');
179
+ const deleteFiles = async ({ ids }) => {
180
+ await models.fileRecord.destroy({
181
+ where: {
182
+ uuid: {
183
+ [Op.in]: ids
156
184
  }
157
- file.filename = filename;
158
- await file.save();
159
- };
185
+ }
186
+ });
187
+ };
160
188
 
161
- Object.assign(services, {
162
- uploadToFileSystem, getFileUrl, getFileInfo, getFileList, deleteFiles, renameFile,
163
- // 兼容之前api,后面可能会删掉
164
- fileRecord: {uploadToFileSystem, getFileUrl, getFileInfo, getFileList, deleteFiles, renameFile}
189
+ const renameFile = async ({ id, filename }) => {
190
+ const file = await models.fileRecord.findOne({
191
+ where: { uuid: id }
165
192
  });
193
+ if (!file) {
194
+ throw new Error('文件不存在');
195
+ }
196
+ file.filename = filename;
197
+ await file.save();
198
+ };
199
+
200
+ Object.assign(services, {
201
+ uploadToFileSystem, uploadFromUrl, getFileUrl, getFileInfo, getFileList, deleteFiles, renameFile,
202
+ // 兼容之前api,后面可能会删掉
203
+ fileRecord: { uploadToFileSystem, uploadFromUrl, getFileUrl, getFileInfo, getFileList, deleteFiles, renameFile }
204
+ });
166
205
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kne/fastify-file-manager",
3
- "version": "2.0.0-alpha.0",
3
+ "version": "2.0.0-alpha.1",
4
4
  "description": "用于管理静态文件上传查看等",
5
5
  "main": "index.js",
6
6
  "scripts": {