使用fflate将文件压缩为Blob供下载时遇异常求助
问题解决:fflate生成损坏ZIP文件的修复方案
核心问题分析
你遇到的ZIP损坏、内容长度异常问题,主要来自两个错误:
- 不必要地使用
ZipPassThrough(流式处理对象)处理完整加载的文件,导致数据推送时机错误 getZipContent中错误地展开chunk为单个数字数组,破坏了二进制数据结构
修正后的完整代码
1. 正确创建ZIP并添加文件
不需要ZipPassThrough,直接用zip.add处理已加载的完整二进制数据:
// 初始化ZIP实例 const zip = new fflate.Zip(); // 添加文件(file为XMLHttpRequest的response) console.log('file.size', file.size); const content = new Uint8Array(await file.arrayBuffer()); // 若需要不压缩(保持原文件大小),添加配置{ level: 0 } zip.add(filename, content);
2. 修复ZIP内容收集逻辑
原代码将chunk展开为单个数字,导致Blob无法正确识别二进制数据,改为合并Uint8Array块:
async function getZipContent(zip) { return new Promise((resolve, reject) => { const chunks = []; zip.ondata = (err, chunk, final) => { if (err) return reject(err); if (chunk) chunks.push(chunk); if (final) { // 合并所有二进制块为单个Uint8Array const totalLength = chunks.reduce((sum, chunk) => sum + chunk.length, 0); const mergedContent = new Uint8Array(totalLength); let offset = 0; for (const chunk of chunks) { mergedContent.set(chunk, offset); offset += chunk.length; } resolve(mergedContent); } }; zip.end(); }); } async function downloadZip(zip) { const content = await getZipContent(zip); console.log('content length', content.length); // 正确指定Blob类型为application/zip const blob = new Blob([content], { type: 'application/zip' }); const a = document.createElement('a'); const blobUrl = URL.createObjectURL(blob); a.href = blobUrl; a.download = 'out.zip'; a.click(); URL.revokeObjectURL(blobUrl); }
为什么之前的方案无效?
- 交换
zip.add与push顺序报错:ZipPassThrough必须先被添加到ZIP实例,才能注册流式处理的handler,所以顺序不能调换。但你场景下根本不需要用流式处理。 - 包裹Uint8Array无效:原代码的chunks是单个数字的数组,即使包裹成Uint8Array,数据已经被破坏,无法还原原始二进制。
- 修改Blob类型无效:核心问题是二进制数据结构错误,类型只是标识,不影响数据本身。
流式处理场景(可选)
如果后续需要处理超大文件、不想一次性加载到内存,可以用ZipPassThrough配合XHR流式加载:
const zip = new fflate.Zip(); const stream = new fflate.ZipPassThrough(filename); zip.add(stream); let prevLoaded = 0; const xhr = new XMLHttpRequest(); xhr.open('GET', fileUrl); xhr.responseType = 'arraybuffer'; xhr.onprogress = (e) => { if (e.loaded > prevLoaded) { const chunk = new Uint8Array(xhr.response.slice(prevLoaded, e.loaded)); stream.push(chunk); prevLoaded = e.loaded; } }; xhr.onload = () => { stream.push(new Uint8Array(xhr.response.slice(prevLoaded)), true); // 之后调用downloadZip(zip) }; xhr.send();
内容的提问来源于stack exchange,提问作者lakazatong
相关产品推荐
相关产品推荐

