fix: ensure proper closure of zip reader and file handles
This commit is contained in:
parent
c5d15240bc
commit
26b55be260
2 changed files with 47 additions and 14 deletions
|
@ -29,7 +29,7 @@ export async function renderComicPage({ path, page, reqHeaders, set }: RenderOpt
|
|||
|
||||
if (page < 0 || page >= entries.length) {
|
||||
set.status = 404;
|
||||
zip.reader.close();
|
||||
await zip.reader.close();
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -45,7 +45,7 @@ export async function renderComicPage({ path, page, reqHeaders, set }: RenderOpt
|
|||
const cachedDate = new Date(ifModifiedSince);
|
||||
if (!Number.isNaN(cachedDate.valueOf()) && lastModified <= cachedDate) {
|
||||
set.status = 304;
|
||||
zip.reader.close();
|
||||
await zip.reader.close();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -57,6 +57,14 @@ export async function renderComicPage({ path, page, reqHeaders, set }: RenderOpt
|
|||
},
|
||||
});
|
||||
|
||||
let zipClosed = false;
|
||||
const closeZip = async () => {
|
||||
if (!zipClosed) {
|
||||
zipClosed = true;
|
||||
await zip.reader.close();
|
||||
}
|
||||
};
|
||||
|
||||
readStream.pipeTo(new WritableStream({
|
||||
write(chunk) {
|
||||
nodeReadable.push(chunk);
|
||||
|
@ -64,15 +72,21 @@ export async function renderComicPage({ path, page, reqHeaders, set }: RenderOpt
|
|||
close() {
|
||||
nodeReadable.push(null);
|
||||
},
|
||||
abort(err) {
|
||||
nodeReadable.destroy(err);
|
||||
},
|
||||
})).catch((err) => {
|
||||
nodeReadable.destroy(err);
|
||||
});
|
||||
|
||||
nodeReadable.on("close", () => {
|
||||
zip.reader.close();
|
||||
closeZip().catch(console.error);
|
||||
});
|
||||
nodeReadable.on("error", () => {
|
||||
zip.reader.close();
|
||||
closeZip().catch(console.error);
|
||||
});
|
||||
nodeReadable.on("end", () => {
|
||||
closeZip().catch(console.error);
|
||||
});
|
||||
|
||||
const ext = entry.filename.split(".").pop()?.toLowerCase() ?? "jpeg";
|
||||
|
@ -84,7 +98,7 @@ export async function renderComicPage({ path, page, reqHeaders, set }: RenderOpt
|
|||
set.status = 200;
|
||||
return nodeReadable;
|
||||
} catch (error) {
|
||||
zip.reader.close();
|
||||
await zip.reader.close();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { type FileHandle, open } from "node:fs/promises";
|
||||
import { orderBy } from "natural-orderby";
|
||||
import { ZipReader, Reader, type Entry, ZipReaderConstructorOptions } from "@zip.js/zip.js";
|
||||
import EventEmitter from "node:events";
|
||||
|
||||
class FileReader extends Reader<string> {
|
||||
private fd?: FileHandle;
|
||||
private path: string;
|
||||
private closed = false;
|
||||
|
||||
constructor(path: string) {
|
||||
super(path);
|
||||
|
@ -14,21 +14,29 @@ class FileReader extends Reader<string> {
|
|||
|
||||
async init(): Promise<void> {
|
||||
await super.init?.();
|
||||
if (this.closed) return;
|
||||
|
||||
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", () => {
|
||||
this.fd?.close();
|
||||
this.fd = undefined;
|
||||
});
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
await this.fd?.close();
|
||||
if (this.closed) return;
|
||||
this.closed = true;
|
||||
|
||||
if (this.fd) {
|
||||
await this.fd.close();
|
||||
this.fd = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
async readUint8Array(index: number, length: number): Promise<Uint8Array> {
|
||||
if (this.closed) {
|
||||
throw new Error("FileReader is closed");
|
||||
}
|
||||
|
||||
try {
|
||||
const buffer = new Uint8Array(length);
|
||||
if (this.fd === undefined) {
|
||||
|
@ -49,18 +57,29 @@ class FileReader extends Reader<string> {
|
|||
return buffer;
|
||||
} catch (error) {
|
||||
console.error("read error", error);
|
||||
// 에러 발생 시 파일 핸들 정리
|
||||
await this.close();
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FileZipReader extends ZipReader<FileHandle> {
|
||||
private closed = false;
|
||||
|
||||
constructor(private reader: FileReader, options?: ZipReaderConstructorOptions) {
|
||||
super(reader, options);
|
||||
}
|
||||
|
||||
override async close(): Promise<void> {
|
||||
super.close();
|
||||
await this.reader.close();
|
||||
if (this.closed) return;
|
||||
this.closed = true;
|
||||
|
||||
try {
|
||||
await super.close();
|
||||
} finally {
|
||||
await this.reader.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue