gongxiangp2/lib/archive-download-stream.ts

66 lines
1.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { Archiver } from 'archiver'
import { Readable } from 'node:stream'
import { nodeReadableToWebStream } from '@/lib/node-readable-stream'
/**
* 管理动态 ZIP 的输出与外部输入流。
*
* Archiver 仅销毁输出流时不会保证取消排队任务或销毁已 append 的远程流,
* 因此下载取消必须同时 abort 压缩队列并销毁所有仍存活的显式输入流。
*/
export function createArchiveDownloadStream(archive: Archiver) {
const trackedSources = new Set<Readable>()
let stopped = false
let finalized = false
const destroyTrackedSources = () => {
trackedSources.forEach((source) => {
if (!source.destroyed) {
source.destroy()
}
})
trackedSources.clear()
}
const abort = () => {
if (stopped) {
return
}
stopped = true
destroyTrackedSources()
try {
archive.abort()
} catch {
// 已结束或已销毁的 archive 仍继续执行下方 destroy,确保输出关闭。
}
if (!archive.destroyed) {
archive.destroy()
}
}
archive.once('error', destroyTrackedSources)
archive.once('end', () => trackedSources.clear())
return {
trackSource<T extends Readable>(source: T): T {
if (!source.destroyed) {
trackedSources.add(source)
source.once('close', () => trackedSources.delete(source))
}
return source
},
abort,
finalize(): ReadableStream<Uint8Array> {
if (finalized) {
throw new Error('ZIP 下载流已经开始生成')
}
finalized = true
const stream = nodeReadableToWebStream(archive, { onCancel: abort })
void archive.finalize().catch(() => abort())
return stream
},
}
}