vvvfs 0.0.1 → 0.0.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/README.md +21 -5
- package/dist/vvvfs.min.js +3 -3
- package/index.html +17 -12
- package/index.ts +433 -81
- package/package.json +1 -1
- package/index.js +0 -142
package/index.ts
CHANGED
|
@@ -2,6 +2,20 @@ import { Dexie, Table } from "dexie";
|
|
|
2
2
|
import packageJson from "./package.json" with { type: "json" };
|
|
3
3
|
import mime from "mime";
|
|
4
4
|
|
|
5
|
+
/**
|
|
6
|
+
* 虚拟文件系统
|
|
7
|
+
* @author IFTC
|
|
8
|
+
* @description 虚拟文件系统
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 文件记录
|
|
13
|
+
* @param id 文件id
|
|
14
|
+
* @param name 文件名
|
|
15
|
+
* @param path 文件路径
|
|
16
|
+
* @param type 文件类型(file或dir)
|
|
17
|
+
* @param file 文件对象
|
|
18
|
+
*/
|
|
5
19
|
export interface FileRecord {
|
|
6
20
|
id?: number;
|
|
7
21
|
name: string;
|
|
@@ -10,14 +24,40 @@ export interface FileRecord {
|
|
|
10
24
|
file: File | null;
|
|
11
25
|
}
|
|
12
26
|
|
|
27
|
+
/**
|
|
28
|
+
* 虚拟文件系统数据库
|
|
29
|
+
* @param files 文件存储数据表
|
|
30
|
+
*/
|
|
13
31
|
export interface VVVFSDatabase extends Dexie {
|
|
14
32
|
files: Table<FileRecord, number>;
|
|
15
33
|
}
|
|
16
34
|
|
|
35
|
+
/**
|
|
36
|
+
* 虚拟文件系统配置项
|
|
37
|
+
* @param throwError 是否抛出错误
|
|
38
|
+
*/
|
|
39
|
+
export interface VVVFSOptions {
|
|
40
|
+
throwError: boolean;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 虚拟文件系统错误类
|
|
45
|
+
*/
|
|
46
|
+
class VVVFSError extends Error {
|
|
47
|
+
/**
|
|
48
|
+
* @param type 错误类型
|
|
49
|
+
* @param message 错误信息
|
|
50
|
+
*/
|
|
51
|
+
constructor(type: string, message: string) {
|
|
52
|
+
super(message);
|
|
53
|
+
this.name = "VVVFS" + type + "Error";
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
17
57
|
const version = packageJson.version;
|
|
18
58
|
class VVVFS {
|
|
19
59
|
private db: VVVFSDatabase;
|
|
20
|
-
options:
|
|
60
|
+
options: VVVFSOptions;
|
|
21
61
|
/**
|
|
22
62
|
* 虚拟文件系统版本
|
|
23
63
|
*/
|
|
@@ -27,22 +67,173 @@ class VVVFS {
|
|
|
27
67
|
* @param name 虚拟文件系统名称
|
|
28
68
|
* @param options 配置项
|
|
29
69
|
*/
|
|
30
|
-
constructor(
|
|
70
|
+
constructor(
|
|
71
|
+
name = "vvvfs",
|
|
72
|
+
options = {
|
|
73
|
+
throwError: false,
|
|
74
|
+
},
|
|
75
|
+
) {
|
|
31
76
|
this.options = options;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
77
|
+
try {
|
|
78
|
+
this.db = new Dexie(name) as VVVFSDatabase;
|
|
79
|
+
this.db.version(1).stores({
|
|
80
|
+
files: "++id, name, path, type, file",
|
|
81
|
+
});
|
|
82
|
+
} catch (error) {
|
|
83
|
+
console.error("创建数据库失败", error);
|
|
84
|
+
throw new VVVFSError("CreateDatabase", "创建数据库失败");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* 初始化虚拟文件系统
|
|
89
|
+
* @description 将Linux的系统初始文件初始化到数据库中
|
|
90
|
+
*/
|
|
91
|
+
async init(user?: string) {
|
|
92
|
+
const linuxInitFiles = [
|
|
93
|
+
{
|
|
94
|
+
name: "root",
|
|
95
|
+
path: "/",
|
|
96
|
+
type: "dir",
|
|
97
|
+
file: new File([], "root"),
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: "boot",
|
|
101
|
+
path: "/",
|
|
102
|
+
type: "dir",
|
|
103
|
+
file: new File([], "boot"),
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: "bin",
|
|
107
|
+
path: "/",
|
|
108
|
+
type: "dir",
|
|
109
|
+
file: new File([], "bin"),
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: "dev",
|
|
113
|
+
path: "/",
|
|
114
|
+
type: "dir",
|
|
115
|
+
file: new File([], "dev"),
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: "etc",
|
|
119
|
+
path: "/",
|
|
120
|
+
type: "dir",
|
|
121
|
+
file: new File([], "etc"),
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: "home",
|
|
125
|
+
path: "/",
|
|
126
|
+
type: "dir",
|
|
127
|
+
file: new File([], "home"),
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: user || "root",
|
|
131
|
+
path: "/home",
|
|
132
|
+
type: "dir",
|
|
133
|
+
file: new File([], "home"),
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: "lib",
|
|
137
|
+
path: "/",
|
|
138
|
+
type: "dir",
|
|
139
|
+
file: new File([], "lib"),
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: "lib64",
|
|
143
|
+
path: "/",
|
|
144
|
+
type: "dir",
|
|
145
|
+
file: new File([], "lib64"),
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: "media",
|
|
149
|
+
path: "/",
|
|
150
|
+
type: "dir",
|
|
151
|
+
file: new File([], "media"),
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: "mnt",
|
|
155
|
+
path: "/",
|
|
156
|
+
type: "dir",
|
|
157
|
+
file: new File([], "mnt"),
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
name: "opt",
|
|
161
|
+
path: "/",
|
|
162
|
+
type: "dir",
|
|
163
|
+
file: new File([], "opt"),
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: "proc",
|
|
167
|
+
path: "/",
|
|
168
|
+
type: "dir",
|
|
169
|
+
file: new File([], "proc"),
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
name: "run",
|
|
173
|
+
path: "/",
|
|
174
|
+
type: "dir",
|
|
175
|
+
file: new File([], "run"),
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: "sbin",
|
|
179
|
+
path: "/",
|
|
180
|
+
type: "dir",
|
|
181
|
+
file: new File([], "sbin"),
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
name: "srv",
|
|
185
|
+
path: "/",
|
|
186
|
+
type: "dir",
|
|
187
|
+
file: new File([], "srv"),
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
name: "sys",
|
|
191
|
+
path: "/",
|
|
192
|
+
type: "dir",
|
|
193
|
+
file: new File([], "sys"),
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
name: "tmp",
|
|
197
|
+
path: "/",
|
|
198
|
+
type: "dir",
|
|
199
|
+
file: new File([], "tmp"),
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: "usr",
|
|
203
|
+
path: "/",
|
|
204
|
+
type: "dir",
|
|
205
|
+
file: new File([], "usr"),
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
name: "var",
|
|
209
|
+
path: "/",
|
|
210
|
+
type: "dir",
|
|
211
|
+
file: new File([], "var"),
|
|
212
|
+
},
|
|
213
|
+
];
|
|
214
|
+
try {
|
|
215
|
+
for (const file of linuxInitFiles) {
|
|
216
|
+
await this.db.files.put(file);
|
|
217
|
+
}
|
|
218
|
+
} catch (error) {
|
|
219
|
+
console.error("初始化文件失败", error);
|
|
220
|
+
throw new VVVFSError("InitFiles", "初始化文件失败" + error);
|
|
221
|
+
}
|
|
36
222
|
}
|
|
37
223
|
/**
|
|
38
224
|
* 重置虚拟文件系统
|
|
39
225
|
*/
|
|
40
226
|
async reset() {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
227
|
+
try {
|
|
228
|
+
await this.db.delete();
|
|
229
|
+
this.db = new Dexie(this.db.name) as VVVFSDatabase;
|
|
230
|
+
this.db.version(1).stores({
|
|
231
|
+
files: "++id, name, path, type, file",
|
|
232
|
+
});
|
|
233
|
+
} catch (error) {
|
|
234
|
+
console.error("重置数据库失败", error);
|
|
235
|
+
throw new VVVFSError("ResetDatabase", "重置数据库失败" + error);
|
|
236
|
+
}
|
|
46
237
|
}
|
|
47
238
|
/**
|
|
48
239
|
* 创建文件
|
|
@@ -58,7 +249,7 @@ class VVVFS {
|
|
|
58
249
|
if (!(await this.exists(parent))) {
|
|
59
250
|
await this.createDir(parent);
|
|
60
251
|
}
|
|
61
|
-
console.log(name, parent);
|
|
252
|
+
// console.log(name, parent);
|
|
62
253
|
await this.db.files.add({
|
|
63
254
|
name: name,
|
|
64
255
|
path: parent,
|
|
@@ -70,6 +261,9 @@ class VVVFS {
|
|
|
70
261
|
return true;
|
|
71
262
|
} catch (error) {
|
|
72
263
|
console.error("创建文件失败", error);
|
|
264
|
+
if (this.options.throwError) {
|
|
265
|
+
throw new VVVFSError("CreateFile", "创建文件失败" + error);
|
|
266
|
+
}
|
|
73
267
|
return false;
|
|
74
268
|
}
|
|
75
269
|
}
|
|
@@ -107,6 +301,9 @@ class VVVFS {
|
|
|
107
301
|
return true;
|
|
108
302
|
} catch (error) {
|
|
109
303
|
console.error("创建目录失败", error);
|
|
304
|
+
if (this.options.throwError) {
|
|
305
|
+
throw new VVVFSError("CreateDir", "创建目录失败" + error);
|
|
306
|
+
}
|
|
110
307
|
return false;
|
|
111
308
|
}
|
|
112
309
|
}
|
|
@@ -115,33 +312,47 @@ class VVVFS {
|
|
|
115
312
|
* @param path 文件路径
|
|
116
313
|
*/
|
|
117
314
|
async exists(path: string) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
(
|
|
121
|
-
.
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
315
|
+
try {
|
|
316
|
+
const { name, parent } = parsePath(path);
|
|
317
|
+
return (
|
|
318
|
+
(await this.db.files
|
|
319
|
+
.where({
|
|
320
|
+
name: name,
|
|
321
|
+
path: parent,
|
|
322
|
+
})
|
|
323
|
+
.count()) > 0
|
|
324
|
+
);
|
|
325
|
+
} catch (error) {
|
|
326
|
+
console.error("判断文件是否存在失败", error);
|
|
327
|
+
if (this.options.throwError) {
|
|
328
|
+
throw new VVVFSError("Exists", "判断文件是否存在失败" + error);
|
|
329
|
+
}
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
127
332
|
}
|
|
128
333
|
/**
|
|
129
334
|
* 读取文件内容
|
|
130
335
|
* @param path 文件路径
|
|
131
336
|
*/
|
|
132
337
|
async write(path: string, content: Blob) {
|
|
133
|
-
if (!(await this.exists(path))) {
|
|
134
|
-
const success = await this.createFile(path);
|
|
135
|
-
if (!success) {
|
|
136
|
-
console.warn("创建文件失败");
|
|
137
|
-
return false;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
338
|
try {
|
|
339
|
+
if (!(await this.exists(path))) {
|
|
340
|
+
const success = await this.createFile(path);
|
|
341
|
+
if (!success) {
|
|
342
|
+
console.warn("创建文件失败");
|
|
343
|
+
if (this.options.throwError) {
|
|
344
|
+
throw new VVVFSError("Write", "创建文件失败");
|
|
345
|
+
}
|
|
346
|
+
return false;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
141
349
|
const { name, parent } = parsePath(path);
|
|
142
350
|
console.log(name, parent);
|
|
143
351
|
if (await this.isDir(parent + "/" + name)) {
|
|
144
352
|
console.warn("路径已存在");
|
|
353
|
+
if (this.options.throwError) {
|
|
354
|
+
throw new VVVFSError("Write", "路径已存在");
|
|
355
|
+
}
|
|
145
356
|
return false;
|
|
146
357
|
}
|
|
147
358
|
const file = new File([content], name, {
|
|
@@ -157,10 +368,16 @@ class VVVFS {
|
|
|
157
368
|
return true;
|
|
158
369
|
} else {
|
|
159
370
|
console.warn("文件记录未找到,无法写入内容");
|
|
371
|
+
if (this.options.throwError) {
|
|
372
|
+
throw new VVVFSError("Write", "文件记录未找到,无法写入内容");
|
|
373
|
+
}
|
|
160
374
|
return false;
|
|
161
375
|
}
|
|
162
376
|
} catch (error) {
|
|
163
377
|
console.error("写入文件失败", error);
|
|
378
|
+
if (this.options.throwError) {
|
|
379
|
+
throw new VVVFSError("Write", "写入文件失败" + error);
|
|
380
|
+
}
|
|
164
381
|
return false;
|
|
165
382
|
}
|
|
166
383
|
}
|
|
@@ -170,8 +387,16 @@ class VVVFS {
|
|
|
170
387
|
* @param content 文本内容
|
|
171
388
|
*/
|
|
172
389
|
async writeText(path: string, content: string) {
|
|
173
|
-
|
|
174
|
-
|
|
390
|
+
try {
|
|
391
|
+
const blob = new Blob([content], { type: "text/plain" });
|
|
392
|
+
return await this.write(path, blob);
|
|
393
|
+
} catch (error) {
|
|
394
|
+
console.error("写入文件失败", error);
|
|
395
|
+
if (this.options.throwError) {
|
|
396
|
+
throw new VVVFSError("Write", "写入文件失败" + error);
|
|
397
|
+
}
|
|
398
|
+
return false;
|
|
399
|
+
}
|
|
175
400
|
}
|
|
176
401
|
/**
|
|
177
402
|
* 写入JSON内容
|
|
@@ -180,22 +405,39 @@ class VVVFS {
|
|
|
180
405
|
* @param format 是否格式化
|
|
181
406
|
*/
|
|
182
407
|
async writeJson(path: string, content: Record<string, any>, format?: boolean) {
|
|
183
|
-
|
|
408
|
+
try {
|
|
409
|
+
return await this.writeText(
|
|
410
|
+
path,
|
|
411
|
+
JSON.stringify(content, null, format ? 4 : undefined),
|
|
412
|
+
);
|
|
413
|
+
} catch (error) {
|
|
414
|
+
console.error("写入文件失败", error);
|
|
415
|
+
if (this.options.throwError) {
|
|
416
|
+
throw new VVVFSError("Write", "写入文件失败" + error);
|
|
417
|
+
}
|
|
418
|
+
return false;
|
|
419
|
+
}
|
|
184
420
|
}
|
|
185
421
|
/**
|
|
186
422
|
* 读取文件内容
|
|
187
423
|
* @param path 文件路径
|
|
188
424
|
*/
|
|
189
425
|
async read(path: string) {
|
|
190
|
-
if (!(await this.exists(path))) {
|
|
191
|
-
console.warn("文件不存在");
|
|
192
|
-
return null;
|
|
193
|
-
}
|
|
194
426
|
try {
|
|
427
|
+
if (!(await this.exists(path))) {
|
|
428
|
+
console.warn("文件不存在");
|
|
429
|
+
if (this.options.throwError) {
|
|
430
|
+
throw new VVVFSError("Read", "文件不存在");
|
|
431
|
+
}
|
|
432
|
+
return null;
|
|
433
|
+
}
|
|
195
434
|
const { name, parent } = parsePath(path);
|
|
196
435
|
return (await this.db.files.where({ name, path: parent }).first())?.file;
|
|
197
436
|
} catch (error) {
|
|
198
437
|
console.error("读取文件失败", error);
|
|
438
|
+
if (this.options.throwError) {
|
|
439
|
+
throw new VVVFSError("Read", "读取文件失败" + error);
|
|
440
|
+
}
|
|
199
441
|
return null;
|
|
200
442
|
}
|
|
201
443
|
}
|
|
@@ -204,10 +446,22 @@ class VVVFS {
|
|
|
204
446
|
* @param path 文件路径
|
|
205
447
|
*/
|
|
206
448
|
async readText(path: string) {
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
449
|
+
try {
|
|
450
|
+
const file = await this.read(path);
|
|
451
|
+
if (file) {
|
|
452
|
+
return await file.text();
|
|
453
|
+
} else {
|
|
454
|
+
console.warn("文件不存在");
|
|
455
|
+
if (this.options.throwError) {
|
|
456
|
+
throw new VVVFSError("Read", "文件不存在");
|
|
457
|
+
}
|
|
458
|
+
return null;
|
|
459
|
+
}
|
|
460
|
+
} catch (error) {
|
|
461
|
+
console.error("读取文件失败", error);
|
|
462
|
+
if (this.options.throwError) {
|
|
463
|
+
throw new VVVFSError("Read", "读取文件失败" + error);
|
|
464
|
+
}
|
|
211
465
|
return null;
|
|
212
466
|
}
|
|
213
467
|
}
|
|
@@ -216,15 +470,26 @@ class VVVFS {
|
|
|
216
470
|
* @param path 文件路径
|
|
217
471
|
*/
|
|
218
472
|
async readJson(path: string) {
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
473
|
+
try {
|
|
474
|
+
const text = await this.readText(path);
|
|
475
|
+
if (text) {
|
|
476
|
+
try {
|
|
477
|
+
return JSON.parse(text);
|
|
478
|
+
} catch (error) {
|
|
479
|
+
console.error("解析JSON失败", error);
|
|
480
|
+
if (this.options.throwError) {
|
|
481
|
+
throw new VVVFSError("Read", "解析JSON失败" + error);
|
|
482
|
+
}
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
} else {
|
|
225
486
|
return null;
|
|
226
487
|
}
|
|
227
|
-
}
|
|
488
|
+
} catch (error) {
|
|
489
|
+
console.error("读取文件失败", error);
|
|
490
|
+
if (this.options.throwError) {
|
|
491
|
+
throw new VVVFSError("Read", "读取文件失败" + error);
|
|
492
|
+
}
|
|
228
493
|
return null;
|
|
229
494
|
}
|
|
230
495
|
}
|
|
@@ -233,24 +498,48 @@ class VVVFS {
|
|
|
233
498
|
* @param path 文件路径
|
|
234
499
|
*/
|
|
235
500
|
async isFile(path: string) {
|
|
236
|
-
|
|
237
|
-
|
|
501
|
+
try {
|
|
502
|
+
const { name, parent } = parsePath(path);
|
|
503
|
+
return (await this.db.files.where({ name, path: parent, type: "file" }).count()) > 0;
|
|
504
|
+
} catch (error) {
|
|
505
|
+
console.error("判断文件类型失败", error);
|
|
506
|
+
if (this.options.throwError) {
|
|
507
|
+
throw new VVVFSError("IsFile", "判断文件类型失败" + error);
|
|
508
|
+
}
|
|
509
|
+
return false;
|
|
510
|
+
}
|
|
238
511
|
}
|
|
239
512
|
/**
|
|
240
513
|
* 判断是否是目录
|
|
241
514
|
* @param path 文件路径
|
|
242
515
|
*/
|
|
243
516
|
async isDir(path: string) {
|
|
244
|
-
|
|
245
|
-
|
|
517
|
+
try {
|
|
518
|
+
const { name, parent } = parsePath(path);
|
|
519
|
+
return (await this.db.files.where({ path: parent, name, type: "dir" }).count()) > 0;
|
|
520
|
+
} catch (error) {
|
|
521
|
+
console.error("判断文件类型失败", error);
|
|
522
|
+
if (this.options.throwError) {
|
|
523
|
+
throw new VVVFSError("IsDir", "判断文件类型失败" + error);
|
|
524
|
+
}
|
|
525
|
+
return false;
|
|
526
|
+
}
|
|
246
527
|
}
|
|
247
528
|
/**
|
|
248
529
|
* 列出目录下的文件
|
|
249
530
|
* @param path 目录路径
|
|
250
531
|
*/
|
|
251
532
|
async list(path: string) {
|
|
252
|
-
|
|
253
|
-
|
|
533
|
+
try {
|
|
534
|
+
const { name, parent } = parsePath(path);
|
|
535
|
+
return (await this.db.files.where({ path: parent }).toArray()).map((file) => file.name).filter((item) => item != "");
|
|
536
|
+
} catch (error) {
|
|
537
|
+
console.error("列出目录下的文件失败", error);
|
|
538
|
+
if (this.options.throwError) {
|
|
539
|
+
throw new VVVFSError("List", "列出目录下的文件失败" + error);
|
|
540
|
+
}
|
|
541
|
+
return [];
|
|
542
|
+
}
|
|
254
543
|
}
|
|
255
544
|
/**
|
|
256
545
|
* 重命名文件
|
|
@@ -258,8 +547,14 @@ class VVVFS {
|
|
|
258
547
|
* @param newName 新文件名
|
|
259
548
|
*/
|
|
260
549
|
async rename(path: string, newName: string) {
|
|
261
|
-
if (!(await this.exists(path))) throw new Error("File not exists");
|
|
262
550
|
try {
|
|
551
|
+
if (!(await this.exists(path))) {
|
|
552
|
+
console.warn("文件不存在");
|
|
553
|
+
if (this.options.throwError) {
|
|
554
|
+
throw new VVVFSError("Rename", "文件不存在");
|
|
555
|
+
}
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
263
558
|
const { name, parent } = parsePath(path);
|
|
264
559
|
const file = await this.db.files.get({ path, name });
|
|
265
560
|
await this.db.files.put({
|
|
@@ -271,6 +566,9 @@ class VVVFS {
|
|
|
271
566
|
return true;
|
|
272
567
|
} catch (e) {
|
|
273
568
|
console.error(e);
|
|
569
|
+
if (this.options.throwError) {
|
|
570
|
+
throw new VVVFSError("Rename", "重命名文件失败" + e);
|
|
571
|
+
}
|
|
274
572
|
return false;
|
|
275
573
|
}
|
|
276
574
|
}
|
|
@@ -279,8 +577,14 @@ class VVVFS {
|
|
|
279
577
|
* @param path 文件路径
|
|
280
578
|
*/
|
|
281
579
|
async delete(path: string) {
|
|
282
|
-
if (!(await this.exists(path))) return false;
|
|
283
580
|
try {
|
|
581
|
+
if (!(await this.exists(path))) {
|
|
582
|
+
console.warn("文件不存在");
|
|
583
|
+
if (this.options.throwError) {
|
|
584
|
+
throw new VVVFSError("Delete", "文件不存在");
|
|
585
|
+
}
|
|
586
|
+
return false;
|
|
587
|
+
}
|
|
284
588
|
const { name, parent } = parsePath(path);
|
|
285
589
|
if (await this.isDir(path)) {
|
|
286
590
|
const files = await this.list(path);
|
|
@@ -301,6 +605,9 @@ class VVVFS {
|
|
|
301
605
|
}
|
|
302
606
|
} catch (e) {
|
|
303
607
|
console.error(e);
|
|
608
|
+
if (this.options.throwError) {
|
|
609
|
+
throw new VVVFSError("Delete", "删除文件失败" + e);
|
|
610
|
+
}
|
|
304
611
|
return false;
|
|
305
612
|
}
|
|
306
613
|
}
|
|
@@ -310,9 +617,21 @@ class VVVFS {
|
|
|
310
617
|
* @param newPath 新路径
|
|
311
618
|
*/
|
|
312
619
|
async move(path: string, newPath: string) {
|
|
313
|
-
if (await this.exists(newPath)) return false;
|
|
314
|
-
if (!(await this.exists(path))) return false;
|
|
315
620
|
try {
|
|
621
|
+
if (await this.exists(newPath)) {
|
|
622
|
+
console.warn("目标文件已存在");
|
|
623
|
+
if (this.options.throwError) {
|
|
624
|
+
throw new VVVFSError("Move", "目标文件已存在");
|
|
625
|
+
}
|
|
626
|
+
return false;
|
|
627
|
+
}
|
|
628
|
+
if (!(await this.exists(path))) {
|
|
629
|
+
console.warn("源文件不存在");
|
|
630
|
+
if (this.options.throwError) {
|
|
631
|
+
throw new VVVFSError("Move", "源文件不存在");
|
|
632
|
+
}
|
|
633
|
+
return false;
|
|
634
|
+
}
|
|
316
635
|
const { name, parent } = parsePath(path);
|
|
317
636
|
const { name: newName, parent: newParent } = parsePath(newPath);
|
|
318
637
|
await this.createDir(newParent);
|
|
@@ -329,6 +648,9 @@ class VVVFS {
|
|
|
329
648
|
return true;
|
|
330
649
|
} catch (e) {
|
|
331
650
|
console.error(e);
|
|
651
|
+
if (this.options.throwError) {
|
|
652
|
+
throw new VVVFSError("Move", "移动文件失败" + e);
|
|
653
|
+
}
|
|
332
654
|
return false;
|
|
333
655
|
}
|
|
334
656
|
}
|
|
@@ -338,9 +660,21 @@ class VVVFS {
|
|
|
338
660
|
* @param newPath 新路径
|
|
339
661
|
*/
|
|
340
662
|
async copy(path: string, newPath: string) {
|
|
341
|
-
if (await this.exists(newPath)) return false;
|
|
342
|
-
if (!(await this.exists(path))) return false;
|
|
343
663
|
try {
|
|
664
|
+
if (await this.exists(newPath)) {
|
|
665
|
+
console.warn("目标文件已存在");
|
|
666
|
+
if (this.options.throwError) {
|
|
667
|
+
throw new VVVFSError("Copy", "目标文件已存在");
|
|
668
|
+
}
|
|
669
|
+
return false;
|
|
670
|
+
}
|
|
671
|
+
if (!(await this.exists(path))) {
|
|
672
|
+
console.warn("源文件不存在");
|
|
673
|
+
if (this.options.throwError) {
|
|
674
|
+
throw new VVVFSError("Copy", "源文件不存在");
|
|
675
|
+
}
|
|
676
|
+
return false;
|
|
677
|
+
}
|
|
344
678
|
const { name, parent } = parsePath(path);
|
|
345
679
|
const { name: newName, parent: newParent } = parsePath(newPath);
|
|
346
680
|
await this.createDir(newParent);
|
|
@@ -356,6 +690,9 @@ class VVVFS {
|
|
|
356
690
|
return true;
|
|
357
691
|
} catch (e) {
|
|
358
692
|
console.error(e);
|
|
693
|
+
if (this.options.throwError) {
|
|
694
|
+
throw new VVVFSError("Copy", "复制文件失败" + e);
|
|
695
|
+
}
|
|
359
696
|
return false;
|
|
360
697
|
}
|
|
361
698
|
}
|
|
@@ -365,33 +702,47 @@ class VVVFS {
|
|
|
365
702
|
* @param query 查询字符串
|
|
366
703
|
*/
|
|
367
704
|
async search(basePath: string, query: string) {
|
|
368
|
-
if (!await this.isDir(basePath)) return null;
|
|
369
|
-
const that = this;
|
|
370
705
|
try {
|
|
706
|
+
if (!(await this.isDir(basePath))) {
|
|
707
|
+
console.warn("基础路径不是目录");
|
|
708
|
+
if (this.options.throwError) {
|
|
709
|
+
throw new VVVFSError("Search", "基础路径不是目录");
|
|
710
|
+
}
|
|
711
|
+
return null;
|
|
712
|
+
}
|
|
713
|
+
const that = this;
|
|
371
714
|
const dirs = await this.list(basePath);
|
|
372
715
|
return await search(basePath, dirs);
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
716
|
+
async function search(parent: string, files: string[]) {
|
|
717
|
+
const result: string[] = [];
|
|
718
|
+
console.log(files);
|
|
719
|
+
for (const file of files) {
|
|
720
|
+
if (file == "") continue;
|
|
721
|
+
console.log(file);
|
|
722
|
+
if (await that.isDir(joinPath(parent, file))) {
|
|
723
|
+
if (file.includes(query)) {
|
|
724
|
+
result.push(joinPath(parent, file) + "/");
|
|
725
|
+
}
|
|
726
|
+
result.push(
|
|
727
|
+
...(await search(
|
|
728
|
+
joinPath(parent, file),
|
|
729
|
+
await that.list(joinPath(parent, file)),
|
|
730
|
+
)),
|
|
731
|
+
);
|
|
732
|
+
} else {
|
|
733
|
+
if (file.includes(query)) {
|
|
734
|
+
result.push(joinPath(parent, file));
|
|
735
|
+
}
|
|
391
736
|
}
|
|
392
737
|
}
|
|
738
|
+
return result;
|
|
393
739
|
}
|
|
394
|
-
|
|
740
|
+
} catch (e) {
|
|
741
|
+
console.error(e);
|
|
742
|
+
if (this.options.throwError) {
|
|
743
|
+
throw new VVVFSError("Search", "搜索文件失败" + e);
|
|
744
|
+
}
|
|
745
|
+
return null;
|
|
395
746
|
}
|
|
396
747
|
}
|
|
397
748
|
}
|
|
@@ -400,11 +751,12 @@ function parsePath(path: string) {
|
|
|
400
751
|
const oldParts = path.split("/");
|
|
401
752
|
const parts: string[] = [];
|
|
402
753
|
for (let i = 0; i < oldParts.length; i++) {
|
|
754
|
+
if (oldParts[i].length > 255) throw new VVVFSError("ParsePath", "文件名过长");
|
|
403
755
|
if (oldParts[i]) parts.push(oldParts[i]);
|
|
404
756
|
}
|
|
405
757
|
const name = parts.pop() || "";
|
|
406
758
|
const parent = "/" + parts.join("/");
|
|
407
|
-
if ((parent
|
|
759
|
+
if (joinPath(parent, name).length > 4096) throw new VVVFSError("ParsePath", "文件路径过长");
|
|
408
760
|
return { name, parent };
|
|
409
761
|
}
|
|
410
762
|
function joinPath(...paths: string[]) {
|
|
@@ -433,7 +785,7 @@ function joinPath(...paths: string[]) {
|
|
|
433
785
|
return result || (isAbsolute ? "/" : ".");
|
|
434
786
|
}
|
|
435
787
|
Object.defineProperty(VVVFS, "version", {
|
|
436
|
-
value:
|
|
788
|
+
value: version,
|
|
437
789
|
writable: false,
|
|
438
790
|
enumerable: true,
|
|
439
791
|
configurable: false,
|