download / ZipDownloader
源码: packages/ui-default/components/zipDownloader/index.ts
使用 streamsaver 创建并流式传输 ZIP 压缩包到浏览器,支持并发文件下载和重试逻辑。
函数
download
ts
async function download(
filename: string,
targets: { filename: string; url?: string; content?: string }[]
): Promise<void>下载包含给定目标文件的 ZIP 压缩包。使用 streamsaver 将 ZIP 直接流式写入磁盘,无需将整个压缩包保存在内存中。文件以并发方式获取(最多 5 个并行),失败时自动重试。
参数:
filename— 生成的 ZIP 文件名(如"Export.zip")。targets— 要包含的文件数组。每个目标必须包含filename以及url(要获取的远程文件)或content(内存中的字符串)之一。
行为:
- 确保浏览器有
WritableStream(必要时加载 polyfill)。 - 通过
streamsaver.createWriteStream创建写入流。 - 以并发数 5 排队所有文件下载。每个失败的下载最多重试 5 次,间隔 3 秒。
- 将 ZIP 流(通过
createZipStream)管道传输到文件流。 - 下载期间通过
beforeunload/unload监听器阻止浏览器关闭。 - 遇到不可恢复的错误时:记录到 Sentry,停止下载,显示错误通知。
downloadProblemSet
ts
async function downloadProblemSet(
pids: number[],
name?: string
): Promise<void>将一个或多个题目导出为 ZIP 压缩包。收集题目元数据、内容、测试数据和附加文件,然后委托给 download。触发 problemset/download 生命周期钩子,以便插件注入额外文件。
参数:
pids— 要导出的题目数字 ID 数组。name(默认"Export")— ZIP 文件的基本名称(变为{name}.zip)。
行为:
- 触发
ctx.serial('problemset/download', pids, name, targets)— 插件可推送额外目标。 - 对每个题目:
- 通过
api('problem', ...)获取题目元数据并序列化为problem.yaml。 - 解析内容:若为 JSON 对象,将每个键拆分为
problem_{key}.md;否则写入problem.md。 - 获取测试数据和附加文件的签名下载链接。
- 通过
- 调用
download(name + '.zip', targets)流式输出 ZIP。 - 出错时:记录到 Sentry 并显示错误通知。
EventMap 扩展
模块在前端 EventMap 上声明了 problemset/download 事件:
ts
interface EventMap {
'problemset/download': (
pids: number[],
name: string,
targets: { filename: string; url?: string; content?: string }[]
) => void;
}插件可通过 ctx.on('problemset/download', ...) 监听,在下载开始前向 ZIP 注入额外文件。