vvvfs 0.1.8 → 0.1.9
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 +12 -0
- package/index.ts +191 -0
- package/package.json +1 -1
- package/types/index.d.ts +44 -0
package/README.md
CHANGED
|
@@ -96,6 +96,12 @@ const vvvfs = new VVVFS("vvvfs", {
|
|
|
96
96
|
|
|
97
97
|
## 更新日志
|
|
98
98
|
|
|
99
|
+
### 0.1.9
|
|
100
|
+
|
|
101
|
+
- 新增 `saveFile` 方法,用于保存文件
|
|
102
|
+
- 新增 `downloadFile` 方法,用于下载文件并保存到IndexedDB(支持`fetch`和`xhr`两种模式,以及下载进度、错误和成功事件)
|
|
103
|
+
- `VVVFS.File` 类新增 `save` 和 `download` 方法
|
|
104
|
+
|
|
99
105
|
### 0.1.8
|
|
100
106
|
|
|
101
107
|
- 新增 `writeJsonValue` 和 `readJsonValue` 方法,用于读取和写入JSON值(键支持字符串、数字数组下标以及嵌套路径,如 `["a", "b"]`)
|
|
@@ -273,6 +279,12 @@ Using the `VVVFS.File` class:
|
|
|
273
279
|
|
|
274
280
|
## Changelog
|
|
275
281
|
|
|
282
|
+
### 0.1.9
|
|
283
|
+
|
|
284
|
+
- Added the `saveFile` method for saving files
|
|
285
|
+
- Added the `downloadFile` method for downloading files to IndexedDB (supports both `fetch` and `xhr` modes, as well as progress, error, and success callbacks)
|
|
286
|
+
- Added the `save` and `download` methods to the `VVVFS.File` class
|
|
287
|
+
|
|
276
288
|
### 0.1.8
|
|
277
289
|
|
|
278
290
|
- Added the `writeJsonValue` and `readJsonValue` methods for reading and writing JSON values (keys support strings, numeric array indexes, and nested paths such as `["a", "b"]`)
|
package/index.ts
CHANGED
|
@@ -44,6 +44,22 @@ export interface VVVFSOptions {
|
|
|
44
44
|
init: boolean | string;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* 虚拟文件系统下载配置项
|
|
49
|
+
* @param headers 请求头
|
|
50
|
+
* @param mode 下载模式(fetch或xhr)
|
|
51
|
+
* @param onProgress 下载进度事件
|
|
52
|
+
* @param onError 下载出错事件
|
|
53
|
+
* @param onSuccess 下载成功事件
|
|
54
|
+
*/
|
|
55
|
+
export interface VVVFSDownloadOptions {
|
|
56
|
+
headers?: Record<string, string>;
|
|
57
|
+
mode?: "fetch" | "xhr";
|
|
58
|
+
onProgress?: (progress: number) => void;
|
|
59
|
+
onError?: (error: any) => void;
|
|
60
|
+
onSuccess?: (success: boolean) => void;
|
|
61
|
+
}
|
|
62
|
+
|
|
47
63
|
/**
|
|
48
64
|
* 虚拟文件系统错误类
|
|
49
65
|
*/
|
|
@@ -289,6 +305,21 @@ class VVVFSFile {
|
|
|
289
305
|
async isLocked() {
|
|
290
306
|
return await this._vvvfs.isLocked(this._path);
|
|
291
307
|
}
|
|
308
|
+
/**
|
|
309
|
+
* 保存文件
|
|
310
|
+
* @param name 文件名
|
|
311
|
+
*/
|
|
312
|
+
async save(name: string) {
|
|
313
|
+
return await this._vvvfs.saveFile(this._path, name);
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* 下载文件
|
|
317
|
+
* @param url 文件链接
|
|
318
|
+
* @param options 下载配置项
|
|
319
|
+
*/
|
|
320
|
+
async download(url: string | URL | Request, options: VVVFSDownloadOptions = {}) {
|
|
321
|
+
return await this._vvvfs.downloadFile(url, this._path, options);
|
|
322
|
+
}
|
|
292
323
|
}
|
|
293
324
|
|
|
294
325
|
/**
|
|
@@ -1297,6 +1328,166 @@ class VVVFS {
|
|
|
1297
1328
|
return await this.#isLocked(targetPath);
|
|
1298
1329
|
}, false);
|
|
1299
1330
|
}
|
|
1331
|
+
/**
|
|
1332
|
+
* 保存文件
|
|
1333
|
+
* @param path 文件路径
|
|
1334
|
+
* @param name 文件名
|
|
1335
|
+
*/
|
|
1336
|
+
async saveFile(path: string, name?: string) {
|
|
1337
|
+
return this.#withErrorHandling("saveFile", async () => {
|
|
1338
|
+
const targetPath = joinPath(path);
|
|
1339
|
+
if (!(await this.#checkAccess(targetPath, "read"))) return false;
|
|
1340
|
+
const file = await this.#internalRead(targetPath);
|
|
1341
|
+
if (!file) {
|
|
1342
|
+
console.warn("文件不存在");
|
|
1343
|
+
return false;
|
|
1344
|
+
}
|
|
1345
|
+
const a = document.createElement("a");
|
|
1346
|
+
a.href = URL.createObjectURL(file);
|
|
1347
|
+
a.download = name || Path.basename(targetPath);
|
|
1348
|
+
a.click();
|
|
1349
|
+
URL.revokeObjectURL(a.href);
|
|
1350
|
+
return true;
|
|
1351
|
+
}, false);
|
|
1352
|
+
}
|
|
1353
|
+
/**
|
|
1354
|
+
* 下载文件
|
|
1355
|
+
* @param url 文件链接
|
|
1356
|
+
* @param path 保存路径
|
|
1357
|
+
* @param options 下载配置项
|
|
1358
|
+
*/
|
|
1359
|
+
async downloadFile(
|
|
1360
|
+
url: string | URL | Request,
|
|
1361
|
+
path: string,
|
|
1362
|
+
options: VVVFSDownloadOptions = {},
|
|
1363
|
+
) {
|
|
1364
|
+
return this.#withErrorHandling("downloadFile", async () => {
|
|
1365
|
+
const targetPath = joinPath(path);
|
|
1366
|
+
if (!(await this.#checkAccess(targetPath, "write"))) return false;
|
|
1367
|
+
const content =
|
|
1368
|
+
(options.mode || "fetch") === "xhr"
|
|
1369
|
+
? await this.#downloadWithXHR(url, options)
|
|
1370
|
+
: await this.#downloadWithFetch(url, options);
|
|
1371
|
+
if (!content) return false;
|
|
1372
|
+
const blob = new Blob([content], {
|
|
1373
|
+
type: mime.getType(targetPath) || "application/octet-stream",
|
|
1374
|
+
});
|
|
1375
|
+
const success = await this.#internalWrite(targetPath, blob);
|
|
1376
|
+
if (success && options.onSuccess) {
|
|
1377
|
+
options.onSuccess(success);
|
|
1378
|
+
}
|
|
1379
|
+
return success;
|
|
1380
|
+
}, false);
|
|
1381
|
+
}
|
|
1382
|
+
/**
|
|
1383
|
+
* 使用fetch下载文件
|
|
1384
|
+
* @param url 文件链接
|
|
1385
|
+
* @param options 下载配置项
|
|
1386
|
+
*/
|
|
1387
|
+
async #downloadWithFetch(
|
|
1388
|
+
url: string | URL | Request,
|
|
1389
|
+
options: VVVFSDownloadOptions,
|
|
1390
|
+
): Promise<Blob | null> {
|
|
1391
|
+
const request =
|
|
1392
|
+
url instanceof Request
|
|
1393
|
+
? url
|
|
1394
|
+
: new Request(url, { headers: options.headers });
|
|
1395
|
+
const response = await fetch(request);
|
|
1396
|
+
if (!response.ok) {
|
|
1397
|
+
const error = new Error(`下载失败,HTTP ${response.status}`);
|
|
1398
|
+
if (options.onError) {
|
|
1399
|
+
options.onError(error);
|
|
1400
|
+
return null;
|
|
1401
|
+
}
|
|
1402
|
+
throw error;
|
|
1403
|
+
}
|
|
1404
|
+
const total = Number(response.headers.get("Content-Length") || 0);
|
|
1405
|
+
if (options.onProgress && response.body && total > 0) {
|
|
1406
|
+
const reader = response.body.getReader();
|
|
1407
|
+
const chunks: Uint8Array<ArrayBuffer>[] = [];
|
|
1408
|
+
let loaded = 0;
|
|
1409
|
+
while (true) {
|
|
1410
|
+
const { done, value } = await reader.read();
|
|
1411
|
+
if (done) break;
|
|
1412
|
+
const chunk = value as Uint8Array<ArrayBuffer>;
|
|
1413
|
+
chunks.push(chunk);
|
|
1414
|
+
loaded += chunk.length;
|
|
1415
|
+
options.onProgress(Math.round((loaded / total) * 100));
|
|
1416
|
+
}
|
|
1417
|
+
return new Blob(chunks);
|
|
1418
|
+
}
|
|
1419
|
+
return await response.blob();
|
|
1420
|
+
}
|
|
1421
|
+
/**
|
|
1422
|
+
* 使用XHR下载文件
|
|
1423
|
+
* @param url 文件链接
|
|
1424
|
+
* @param options 下载配置项
|
|
1425
|
+
*/
|
|
1426
|
+
#downloadWithXHR(
|
|
1427
|
+
url: string | URL | Request,
|
|
1428
|
+
options: VVVFSDownloadOptions,
|
|
1429
|
+
): Promise<Blob | null> {
|
|
1430
|
+
return new Promise((resolve, reject) => {
|
|
1431
|
+
const requestUrl =
|
|
1432
|
+
url instanceof URL
|
|
1433
|
+
? url.href
|
|
1434
|
+
: url instanceof Request
|
|
1435
|
+
? url.url
|
|
1436
|
+
: url;
|
|
1437
|
+
const headers = url instanceof Request ? url.headers : options.headers;
|
|
1438
|
+
const xhr = new XMLHttpRequest();
|
|
1439
|
+
xhr.open("GET", requestUrl);
|
|
1440
|
+
if (headers instanceof Headers) {
|
|
1441
|
+
headers.forEach((value, key) => {
|
|
1442
|
+
xhr.setRequestHeader(key, value);
|
|
1443
|
+
});
|
|
1444
|
+
} else if (headers) {
|
|
1445
|
+
for (const [key, value] of Object.entries(headers)) {
|
|
1446
|
+
xhr.setRequestHeader(key, value);
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
xhr.responseType = "blob";
|
|
1450
|
+
xhr.onprogress = (event) => {
|
|
1451
|
+
if (options.onProgress && event.total > 0) {
|
|
1452
|
+
options.onProgress(
|
|
1453
|
+
Math.round((event.loaded / event.total) * 100),
|
|
1454
|
+
);
|
|
1455
|
+
}
|
|
1456
|
+
};
|
|
1457
|
+
xhr.onload = () => {
|
|
1458
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
1459
|
+
resolve(xhr.response as Blob);
|
|
1460
|
+
} else {
|
|
1461
|
+
const error = new Error(`下载失败,HTTP ${xhr.status}`);
|
|
1462
|
+
if (options.onError) {
|
|
1463
|
+
options.onError(error);
|
|
1464
|
+
resolve(null);
|
|
1465
|
+
} else {
|
|
1466
|
+
reject(error);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
};
|
|
1470
|
+
xhr.onerror = () => {
|
|
1471
|
+
const error = new Error("下载失败");
|
|
1472
|
+
if (options.onError) {
|
|
1473
|
+
options.onError(error);
|
|
1474
|
+
resolve(null);
|
|
1475
|
+
} else {
|
|
1476
|
+
reject(error);
|
|
1477
|
+
}
|
|
1478
|
+
};
|
|
1479
|
+
try {
|
|
1480
|
+
xhr.send();
|
|
1481
|
+
} catch (error) {
|
|
1482
|
+
if (options.onError) {
|
|
1483
|
+
options.onError(error);
|
|
1484
|
+
resolve(null);
|
|
1485
|
+
} else {
|
|
1486
|
+
reject(error);
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1489
|
+
});
|
|
1490
|
+
}
|
|
1300
1491
|
}
|
|
1301
1492
|
/**
|
|
1302
1493
|
* 解析路径
|
package/package.json
CHANGED
package/types/index.d.ts
CHANGED
|
@@ -62,6 +62,22 @@ export interface VVVFSOptions {
|
|
|
62
62
|
init: boolean | string;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
/**
|
|
66
|
+
* 虚拟文件系统下载配置项
|
|
67
|
+
* @param headers 请求头
|
|
68
|
+
* @param mode 下载模式(fetch或xhr)
|
|
69
|
+
* @param onProgress 下载进度事件
|
|
70
|
+
* @param onError 下载出错事件
|
|
71
|
+
* @param onSuccess 下载成功事件
|
|
72
|
+
*/
|
|
73
|
+
export interface VVVFSDownloadOptions {
|
|
74
|
+
headers?: Record<string, string>;
|
|
75
|
+
mode?: "fetch" | "xhr";
|
|
76
|
+
onProgress?: (progress: number) => void;
|
|
77
|
+
onError?: (error: any) => void;
|
|
78
|
+
onSuccess?: (success: boolean) => void;
|
|
79
|
+
}
|
|
80
|
+
|
|
65
81
|
/**
|
|
66
82
|
* 虚拟文件系统错误类
|
|
67
83
|
*/
|
|
@@ -225,6 +241,17 @@ export class VVVFSFile {
|
|
|
225
241
|
* 判断文件是否已锁定
|
|
226
242
|
*/
|
|
227
243
|
isLocked(): Promise<boolean>;
|
|
244
|
+
/**
|
|
245
|
+
* 保存文件
|
|
246
|
+
* @param name 文件名
|
|
247
|
+
*/
|
|
248
|
+
save(name: string): Promise<boolean>;
|
|
249
|
+
/**
|
|
250
|
+
* 下载文件
|
|
251
|
+
* @param url 文件链接
|
|
252
|
+
* @param options 下载配置项
|
|
253
|
+
*/
|
|
254
|
+
download(url: string | URL | Request, options?: VVVFSDownloadOptions): Promise<boolean>;
|
|
228
255
|
}
|
|
229
256
|
|
|
230
257
|
/**
|
|
@@ -532,6 +559,23 @@ export class VVVFS {
|
|
|
532
559
|
* @param path 文件路径
|
|
533
560
|
*/
|
|
534
561
|
isLocked(path: string): Promise<boolean>;
|
|
562
|
+
/**
|
|
563
|
+
* 保存文件
|
|
564
|
+
* @param path 文件路径
|
|
565
|
+
* @param name 文件名
|
|
566
|
+
*/
|
|
567
|
+
saveFile(path: string, name?: string): Promise<boolean>;
|
|
568
|
+
/**
|
|
569
|
+
* 下载文件
|
|
570
|
+
* @param url 文件链接
|
|
571
|
+
* @param path 保存路径
|
|
572
|
+
* @param options 下载配置项
|
|
573
|
+
*/
|
|
574
|
+
downloadFile(
|
|
575
|
+
url: string | URL | Request,
|
|
576
|
+
path: string,
|
|
577
|
+
options?: VVVFSDownloadOptions,
|
|
578
|
+
): Promise<boolean>;
|
|
535
579
|
}
|
|
536
580
|
|
|
537
581
|
/**
|