diff --git a/packages/server/src/route/comic.ts b/packages/server/src/route/comic.ts index 30663e5..f2193c1 100644 --- a/packages/server/src/route/comic.ts +++ b/packages/server/src/route/comic.ts @@ -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;