You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用fflate将文件压缩为Blob供下载时遇异常求助

问题解决:fflate生成损坏ZIP文件的修复方案

核心问题分析

你遇到的ZIP损坏、内容长度异常问题,主要来自两个错误:

  1. 不必要地使用ZipPassThrough(流式处理对象)处理完整加载的文件,导致数据推送时机错误
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 12:44:53