feat: file reader rework
This commit is contained in:
parent
f8e2930ec1
commit
b3f0f6d980
4 changed files with 77 additions and 44 deletions
|
@ -12,7 +12,7 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@std/async": "npm:@jsr/std__async@^1.0.12",
|
"@std/async": "npm:@jsr/std__async@^1.0.12",
|
||||||
"@zip.js/zip.js": "^2.7.52",
|
"@zip.js/zip.js": "^2.7.60",
|
||||||
"better-sqlite3": "^9.6.0",
|
"better-sqlite3": "^9.6.0",
|
||||||
"chokidar": "^3.6.0",
|
"chokidar": "^3.6.0",
|
||||||
"dbtype": "workspace:dbtype",
|
"dbtype": "workspace:dbtype",
|
||||||
|
@ -33,7 +33,7 @@
|
||||||
"@types/koa-bodyparser": "^4.3.12",
|
"@types/koa-bodyparser": "^4.3.12",
|
||||||
"@types/koa-compose": "^3.2.8",
|
"@types/koa-compose": "^3.2.8",
|
||||||
"@types/koa-router": "^7.4.8",
|
"@types/koa-router": "^7.4.8",
|
||||||
"@types/node": "^22.7.4",
|
"@types/node": "^22.15.3",
|
||||||
"@types/tiny-async-pool": "^1.0.5",
|
"@types/tiny-async-pool": "^1.0.5",
|
||||||
"nodemon": "^3.1.7",
|
"nodemon": "^3.1.7",
|
||||||
"tsx": "^4.19.1",
|
"tsx": "^4.19.1",
|
||||||
|
|
|
@ -12,7 +12,6 @@ import { Readable } from "node:stream";
|
||||||
*/
|
*/
|
||||||
const ZipStreamCache = new Map<string, {
|
const ZipStreamCache = new Map<string, {
|
||||||
reader: ZipReader<FileHandle>,
|
reader: ZipReader<FileHandle>,
|
||||||
handle: FileHandle,
|
|
||||||
refCount: number,
|
refCount: number,
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
@ -39,7 +38,6 @@ async function acquireZip(path: string, marked = false) {
|
||||||
// if the cache is not updated, set the new one.
|
// if the cache is not updated, set the new one.
|
||||||
ZipStreamCache.set(path, {
|
ZipStreamCache.set(path, {
|
||||||
reader: obj.reader,
|
reader: obj.reader,
|
||||||
handle: obj.handle,
|
|
||||||
refCount: 1,
|
refCount: 1,
|
||||||
});
|
});
|
||||||
return obj.reader;
|
return obj.reader;
|
||||||
|
@ -57,10 +55,8 @@ function releaseZip(path: string) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (obj.refCount === 1) {
|
if (obj.refCount === 1) {
|
||||||
const { reader, handle } = obj;
|
const { reader } = obj;
|
||||||
reader.close().then(() => {
|
reader.close();
|
||||||
handle.close();
|
|
||||||
});
|
|
||||||
ZipStreamCache.delete(path);
|
ZipStreamCache.delete(path);
|
||||||
} else {
|
} else {
|
||||||
obj.refCount--;
|
obj.refCount--;
|
||||||
|
|
|
@ -1,42 +1,67 @@
|
||||||
import { type FileHandle, open } from "node:fs/promises";
|
import { type FileHandle, open } from "node:fs/promises";
|
||||||
import { orderBy } from "natural-orderby";
|
import { orderBy } from "natural-orderby";
|
||||||
import { ZipReader, Reader, type Entry } from "@zip.js/zip.js";
|
import { ZipReader, Reader, type Entry } from "@zip.js/zip.js";
|
||||||
|
import EventEmitter from "node:events";
|
||||||
|
|
||||||
class FileReader extends Reader<FileHandle> {
|
class FileReader extends Reader<string> {
|
||||||
private fd: FileHandle;
|
private fd?: FileHandle;
|
||||||
constructor(fd: FileHandle) {
|
private path: string;
|
||||||
super(fd);
|
|
||||||
this.fd = fd;
|
constructor(path: string) {
|
||||||
|
super(path);
|
||||||
|
this.path = path;
|
||||||
}
|
}
|
||||||
|
|
||||||
async init(): Promise<void> {
|
async init(): Promise<void> {
|
||||||
this.size = (await this.fd.stat()).size;
|
await super.init?.();
|
||||||
|
const fd = await open(this.path, "r");
|
||||||
|
const stat = await fd.stat();
|
||||||
|
this.fd = fd;
|
||||||
|
this.size = stat.size;
|
||||||
|
// not implemented yet
|
||||||
|
(this.fd as unknown as EventEmitter).on("close", () => {
|
||||||
|
console.warn(`file handle closed: ${this.path}`);
|
||||||
|
this.fd = undefined;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
close(): void {
|
async close(): Promise<void> {
|
||||||
this.fd.close();
|
await this.fd?.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
async readUint8Array(index: number, length: number): Promise<Uint8Array> {
|
async readUint8Array(index: number, length: number): Promise<Uint8Array> {
|
||||||
const buffer = new Uint8Array(length);
|
try {
|
||||||
const buf = await this.fd.read(buffer, 0, length, index);
|
const buffer = new Uint8Array(length);
|
||||||
if (buf.bytesRead !== length) {
|
if (this.fd === undefined) {
|
||||||
console.error(`read error: ${buf.bytesRead} !== ${length}`);
|
console.error("file handle is undefined", this.path);
|
||||||
throw new Error("read error");
|
// reopen the file handle
|
||||||
|
this.fd = await open(this.path, "r");
|
||||||
|
const stat = await this.fd.stat();
|
||||||
|
if (stat.size !== this.size) {
|
||||||
|
console.error("file size changed", this.path, stat.size, this.size);
|
||||||
|
throw new Error("file size changed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const buf = await this.fd.read(buffer, 0, length, index);
|
||||||
|
if (buf.bytesRead !== length) {
|
||||||
|
console.error(`read error: ${buf.bytesRead} !== ${length}`);
|
||||||
|
throw new Error("read error");
|
||||||
|
}
|
||||||
|
return buffer;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("read error", error);
|
||||||
|
throw error;
|
||||||
}
|
}
|
||||||
return buffer;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function readZip(path: string): Promise<{
|
export async function readZip(path: string): Promise<{
|
||||||
reader: ZipReader<FileHandle>
|
reader: ZipReader<FileHandle>
|
||||||
handle: FileHandle
|
|
||||||
}> {
|
}> {
|
||||||
const fd = await open(path, "r");
|
const reader = new ZipReader(new FileReader(path), {
|
||||||
const reader = new ZipReader(new FileReader(fd), {
|
|
||||||
useCompressionStream: true,
|
useCompressionStream: true,
|
||||||
preventClose: false,
|
preventClose: false,
|
||||||
});
|
});
|
||||||
return { reader, handle: fd };
|
return { reader };
|
||||||
}
|
}
|
||||||
export async function entriesByNaturalOrder(zip: ZipReader<FileHandle>) {
|
export async function entriesByNaturalOrder(zip: ZipReader<FileHandle>) {
|
||||||
const entries = await zip.getEntries();
|
const entries = await zip.getEntries();
|
||||||
|
|
46
pnpm-lock.yaml
generated
46
pnpm-lock.yaml
generated
|
@ -173,8 +173,8 @@ importers:
|
||||||
specifier: npm:@jsr/std__async@^1.0.12
|
specifier: npm:@jsr/std__async@^1.0.12
|
||||||
version: '@jsr/std__async@1.0.12'
|
version: '@jsr/std__async@1.0.12'
|
||||||
'@zip.js/zip.js':
|
'@zip.js/zip.js':
|
||||||
specifier: ^2.7.52
|
specifier: ^2.7.60
|
||||||
version: 2.7.52
|
version: 2.7.60
|
||||||
better-sqlite3:
|
better-sqlite3:
|
||||||
specifier: ^9.6.0
|
specifier: ^9.6.0
|
||||||
version: 9.6.0
|
version: 9.6.0
|
||||||
|
@ -231,8 +231,8 @@ importers:
|
||||||
specifier: ^7.4.8
|
specifier: ^7.4.8
|
||||||
version: 7.4.8
|
version: 7.4.8
|
||||||
'@types/node':
|
'@types/node':
|
||||||
specifier: ^22.7.4
|
specifier: ^22.15.3
|
||||||
version: 22.7.4
|
version: 22.15.3
|
||||||
'@types/tiny-async-pool':
|
'@types/tiny-async-pool':
|
||||||
specifier: ^1.0.5
|
specifier: ^1.0.5
|
||||||
version: 1.0.5
|
version: 1.0.5
|
||||||
|
@ -1468,6 +1468,9 @@ packages:
|
||||||
'@types/mime@1.3.5':
|
'@types/mime@1.3.5':
|
||||||
resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==}
|
resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==}
|
||||||
|
|
||||||
|
'@types/node@22.15.3':
|
||||||
|
resolution: {integrity: sha512-lX7HFZeHf4QG/J7tBZqrCAXwz9J5RD56Y6MpP0eJkka8p+K0RY/yBTW7CYFJ4VGCclxqOLKmiGP5juQc6MKgcw==}
|
||||||
|
|
||||||
'@types/node@22.7.4':
|
'@types/node@22.7.4':
|
||||||
resolution: {integrity: sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==}
|
resolution: {integrity: sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==}
|
||||||
|
|
||||||
|
@ -1591,8 +1594,8 @@ packages:
|
||||||
'@vitest/utils@2.1.2':
|
'@vitest/utils@2.1.2':
|
||||||
resolution: {integrity: sha512-zMO2KdYy6mx56btx9JvAqAZ6EyS3g49krMPPrgOp1yxGZiA93HumGk+bZ5jIZtOg5/VBYl5eBmGRQHqq4FG6uQ==}
|
resolution: {integrity: sha512-zMO2KdYy6mx56btx9JvAqAZ6EyS3g49krMPPrgOp1yxGZiA93HumGk+bZ5jIZtOg5/VBYl5eBmGRQHqq4FG6uQ==}
|
||||||
|
|
||||||
'@zip.js/zip.js@2.7.52':
|
'@zip.js/zip.js@2.7.60':
|
||||||
resolution: {integrity: sha512-+5g7FQswvrCHwYKNMd/KFxZSObctLSsQOgqBSi0LzwHo3li9Eh1w5cF5ndjQw9Zbr3ajVnd2+XyiX85gAetx1Q==}
|
resolution: {integrity: sha512-vA3rLyqdxBrVo1FWSsbyoecaqWTV+vgPRf0QKeM7kVDG0r+lHUqd7zQDv1TO9k4BcAoNzNDSNrrel24Mk6addA==}
|
||||||
engines: {bun: '>=0.7.0', deno: '>=1.0.0', node: '>=16.5.0'}
|
engines: {bun: '>=0.7.0', deno: '>=1.0.0', node: '>=16.5.0'}
|
||||||
|
|
||||||
accepts@1.3.8:
|
accepts@1.3.8:
|
||||||
|
@ -3357,6 +3360,9 @@ packages:
|
||||||
undici-types@6.19.8:
|
undici-types@6.19.8:
|
||||||
resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
|
resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==}
|
||||||
|
|
||||||
|
undici-types@6.21.0:
|
||||||
|
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
||||||
|
|
||||||
universalify@2.0.1:
|
universalify@2.0.1:
|
||||||
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
|
resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
|
||||||
engines: {node: '>= 10.0.0'}
|
engines: {node: '>= 10.0.0'}
|
||||||
|
@ -4521,11 +4527,11 @@ snapshots:
|
||||||
|
|
||||||
'@types/accepts@1.3.7':
|
'@types/accepts@1.3.7':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.7.4
|
'@types/node': 22.15.3
|
||||||
|
|
||||||
'@types/better-sqlite3@7.6.11':
|
'@types/better-sqlite3@7.6.11':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.7.4
|
'@types/node': 22.15.3
|
||||||
|
|
||||||
'@types/better-sqlite3@7.6.9':
|
'@types/better-sqlite3@7.6.9':
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -4534,11 +4540,11 @@ snapshots:
|
||||||
'@types/body-parser@1.19.5':
|
'@types/body-parser@1.19.5':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/connect': 3.4.38
|
'@types/connect': 3.4.38
|
||||||
'@types/node': 22.7.4
|
'@types/node': 22.15.3
|
||||||
|
|
||||||
'@types/connect@3.4.38':
|
'@types/connect@3.4.38':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.7.4
|
'@types/node': 22.15.3
|
||||||
|
|
||||||
'@types/content-disposition@0.5.8': {}
|
'@types/content-disposition@0.5.8': {}
|
||||||
|
|
||||||
|
@ -4547,7 +4553,7 @@ snapshots:
|
||||||
'@types/connect': 3.4.38
|
'@types/connect': 3.4.38
|
||||||
'@types/express': 5.0.0
|
'@types/express': 5.0.0
|
||||||
'@types/keygrip': 1.0.6
|
'@types/keygrip': 1.0.6
|
||||||
'@types/node': 22.7.4
|
'@types/node': 22.15.3
|
||||||
|
|
||||||
'@types/d3-array@3.2.1': {}
|
'@types/d3-array@3.2.1': {}
|
||||||
|
|
||||||
|
@ -4577,7 +4583,7 @@ snapshots:
|
||||||
|
|
||||||
'@types/express-serve-static-core@5.0.0':
|
'@types/express-serve-static-core@5.0.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.7.4
|
'@types/node': 22.15.3
|
||||||
'@types/qs': 6.9.16
|
'@types/qs': 6.9.16
|
||||||
'@types/range-parser': 1.2.7
|
'@types/range-parser': 1.2.7
|
||||||
'@types/send': 0.17.4
|
'@types/send': 0.17.4
|
||||||
|
@ -4595,7 +4601,7 @@ snapshots:
|
||||||
|
|
||||||
'@types/jsonwebtoken@8.5.9':
|
'@types/jsonwebtoken@8.5.9':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 22.7.4
|
'@types/node': 22.15.3
|
||||||
|
|
||||||
'@types/keygrip@1.0.6': {}
|
'@types/keygrip@1.0.6': {}
|
||||||
|
|
||||||
|
@ -4620,10 +4626,14 @@ snapshots:
|
||||||
'@types/http-errors': 2.0.4
|
'@types/http-errors': 2.0.4
|
||||||
'@types/keygrip': 1.0.6
|
'@types/keygrip': 1.0.6
|
||||||
'@types/koa-compose': 3.2.8
|
'@types/koa-compose': 3.2.8
|
||||||
'@types/node': 22.7.4
|
'@types/node': 22.15.3
|
||||||
|
|
||||||
'@types/mime@1.3.5': {}
|
'@types/mime@1.3.5': {}
|
||||||
|
|
||||||
|
'@types/node@22.15.3':
|
||||||
|
dependencies:
|
||||||
|
undici-types: 6.21.0
|
||||||
|
|
||||||
'@types/node@22.7.4':
|
'@types/node@22.7.4':
|
||||||
dependencies:
|
dependencies:
|
||||||
undici-types: 6.19.8
|
undici-types: 6.19.8
|
||||||
|
@ -4646,12 +4656,12 @@ snapshots:
|
||||||
'@types/send@0.17.4':
|
'@types/send@0.17.4':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/mime': 1.3.5
|
'@types/mime': 1.3.5
|
||||||
'@types/node': 22.7.4
|
'@types/node': 22.15.3
|
||||||
|
|
||||||
'@types/serve-static@1.15.7':
|
'@types/serve-static@1.15.7':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/http-errors': 2.0.4
|
'@types/http-errors': 2.0.4
|
||||||
'@types/node': 22.7.4
|
'@types/node': 22.15.3
|
||||||
'@types/send': 0.17.4
|
'@types/send': 0.17.4
|
||||||
|
|
||||||
'@types/tiny-async-pool@1.0.5': {}
|
'@types/tiny-async-pool@1.0.5': {}
|
||||||
|
@ -4786,7 +4796,7 @@ snapshots:
|
||||||
loupe: 3.1.2
|
loupe: 3.1.2
|
||||||
tinyrainbow: 1.2.0
|
tinyrainbow: 1.2.0
|
||||||
|
|
||||||
'@zip.js/zip.js@2.7.52': {}
|
'@zip.js/zip.js@2.7.60': {}
|
||||||
|
|
||||||
accepts@1.3.8:
|
accepts@1.3.8:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -6564,6 +6574,8 @@ snapshots:
|
||||||
|
|
||||||
undici-types@6.19.8: {}
|
undici-types@6.19.8: {}
|
||||||
|
|
||||||
|
undici-types@6.21.0: {}
|
||||||
|
|
||||||
universalify@2.0.1: {}
|
universalify@2.0.1: {}
|
||||||
|
|
||||||
unpipe@1.0.0: {}
|
unpipe@1.0.0: {}
|
||||||
|
|
Loading…
Add table
Reference in a new issue