JSZip下载Firestore文件夹时压缩包累积包含历史文件问题
问题背景
尝试下载Firestore中指定文件夹内的文件时,基础下载流程可正常运行,但存在两个异常:
- 遍历每个文件夹生成zip压缩包时,每个下载的压缩包都会累积包含之前文件夹的文件,疑似reduce函数使用方式有误
- 如果不使用reduce函数,所有下载的压缩包均为空
原实现代码
const DownloadFolders = async () => { const jszip = new JSZip(); const storage = getStorage(); folderNumber.map(async (num: any) => { const folder = await listAll(ref(storage, `/folders/folder${num}`)); const proms = await folder.items .map(async (item: any) => { const file = await getMetadata(item); const fileRef = ref(storage, item.fullPath); const fileBlob = await getDownloadURL(fileRef).then((url) => { return fetch(url).then((response) => response.blob()); }); jszip.file(file.name, fileBlob); }) .reduce((previousValue, currentValue) => currentValue); await proms; const blob: any = folder.items.length !== 0 && (await jszip.generateAsync({ type: "blob" })); FileSaver.saveAs(blob, `folder${rot}.zip`); }); };
当前异常表现
- folder1.zip包含file1
- folder2.zip同时包含file1和file2
- 后续压缩包以此类推持续累积之前文件夹的文件
预期效果
- folder1.zip仅包含file1
- folder2.zip仅包含file2
- 每个压缩包仅包含对应文件夹自身的文件
问题根因
代码存在三处核心错误:
- JSZip实例声明位置错误:
jszip实例在循环外初始化,所有循环共用同一个实例,每次添加的文件都会一直存在于实例中,不会自动清空,直接导致文件跨文件夹累积。 - Promise等待逻辑错误:当前写法的reduce只会返回map生成的Promise数组中的最后一个Promise,根本没有等待当前文件夹下所有文件下载完成;如果去掉reduce直接await数组,相当于await一个非Promise值,会直接同步执行后续生成zip的逻辑,此时文件还没下载完,自然生成空压缩包。
- 变量名笔误:保存文件时使用了未定义的变量
rot,正确的循环变量应为num。
修复方案
修复逻辑如下:
- 将JSZip实例的初始化移动到循环内部,每个文件夹对应独立的zip实例,从根源避免文件混杂
- 使用
Promise.all()等待当前文件夹下所有文件下载、添加到zip实例完成后,再执行压缩包生成和下载逻辑 - 修正文件名的变量笔误
- 替换不适合异步场景的
map循环为for...of,避免循环内异步逻辑脱离管控
修复后的完整代码:
const DownloadFolders = async () => { const storage = getStorage(); for (const num of folderNumber) { // 每个文件夹单独创建zip实例 const jszip = new JSZip(); const folder = await listAll(ref(storage, `/folders/folder${num}`)); // 空文件夹直接跳过 if (folder.items.length === 0) continue; // 等待当前文件夹所有文件下载并添加到zip await Promise.all( folder.items.map(async (item) => { const file = await getMetadata(item); const fileRef = ref(storage, item.fullPath); const fileUrl = await getDownloadURL(fileRef); const fileBlob = await fetch(fileUrl).then(res => res.blob()); jszip.file(file.name, fileBlob); }) ); // 生成压缩包并下载 const zipBlob = await jszip.generateAsync({ type: "blob" }); FileSaver.saveAs(zipBlob, `folder${num}.zip`); } };
内容的提问来源于stack exchange,提问作者taha maatof
相关产品推荐
相关产品推荐

