refactor: simplify stream handling and ensure zip closure in renderComicPage function

This commit is contained in:
monoid 2025-10-04 03:35:30 +09:00
parent 837c87fba4
commit d6dae83f20

View file

@ -51,44 +51,7 @@ export async function renderComicPage({ path, page, reqHeaders, set }: RenderOpt
}
const readStream = await createReadableStreamFromZip(zip.reader, entry);
const nodeReadable = new Readable({
read() {
// noop
},
});
let zipClosed = false;
const closeZip = async () => {
if (!zipClosed) {
zipClosed = true;
await zip.reader.close();
}
};
readStream.pipeTo(new WritableStream({
write(chunk) {
nodeReadable.push(chunk);
},
close() {
nodeReadable.push(null);
},
abort(err) {
nodeReadable.destroy(err);
},
})).catch((err) => {
nodeReadable.destroy(err);
});
nodeReadable.on("close", () => {
closeZip().catch(console.error);
});
nodeReadable.on("error", () => {
closeZip().catch(console.error);
});
nodeReadable.on("end", () => {
closeZip().catch(console.error);
});
const ext = entry.filename.split(".").pop()?.toLowerCase() ?? "jpeg";
headers["Content-Type"] = extensionToMime(ext);
if (typeof entry.uncompressedSize === "number") {
@ -96,7 +59,31 @@ export async function renderComicPage({ path, page, reqHeaders, set }: RenderOpt
}
set.status = 200;
return nodeReadable;
// Ensure zip file is closed after stream ends
const streamWithCleanup = new ReadableStream({
async start(controller) {
try {
const reader = readStream.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
controller.enqueue(value);
}
controller.close();
} catch (error) {
controller.error(error);
throw error;
} finally {
await zip.reader.close();
}
},
cancel: async () => {
await zip.reader.close();
}
});
return streamWithCleanup
} catch (error) {
await zip.reader.close();
throw error;