javascript技巧

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript技巧 > 前端处理ZIP

前端进行ZIP处理的方法实现与对比(JSZipvsfflate)

作者:Null155

在前端开发中,处理 ZIP 压缩文件的需求日益增多,无论是​​优化资源加载速度​​还是实现​​文件批量上传/下载​​,本文将为大家介绍两种不同方法,希望对大家有所帮助

1. 引言:前端为何要处理 ZIP

在前端开发中,处理 ZIP 压缩文件的需求日益增多。无论是​​优化资源加载速度​​、​​减少带宽消耗​​,还是实现​​文件批量上传/下载​​,ZIP 压缩技术都能发挥重要作用。通过压缩,我们可以将多个文件合并为一个,减少 HTTP 请求次数,并显著减小传输文件的大小,从而提升用户体验。

2. 库的选择:JSZip 与 fflate 全方位对比

2.1 JSZip:功能全面的老牌选择

JSZip 是一个成熟的 JavaScript 库,用于创建、读取和编辑 ZIP 文件,支持浏览器和 Node.js 环境。

核心优势:​

主要局限:​

2.2 fflate:极致性能的现代选择

fflate 是一个快速、轻量级且纯 JavaScript 实现的压缩库,专注于高性能的压缩和解压缩操作。

核心优势:​

主要局限:​

2.3 综合对比表格

特性JSZipfflate
​​API 易用性​​✅ 高阶,开箱即用❌ 相对底层,需自行处理数据
​​功能丰富度​​✅ 创建、读取、编辑 ZIP✅❌ 专注压缩/解压
​​性能表现​​❌ 较慢,纯 JS 实现✅ 极快,TypedArray 优化
​​内存效率​​❌ 全文件加载内存✅ 高效,支持流式
​​社区生态​​✅ 文档丰富,示例多❌ 相对较新,资源少
​​体积​​❌ 相对较大✅ 极轻量(几 KB)
​​适用场景​​常规 ZIP 操作、快速开发高性能需求、大文件处理

3. 实战代码:解压与压缩

3.1 使用 JSZip 解压 ZIP 文件

JSZip 的异步 API 和直观的文件访问方式使其解压过程非常清晰。

import JSZip from 'jszip';

const jszip = new JSZip();

async function decompressWithJSZip(file: File) {
    // 加载ZIP文件
    const zip = await jszip.loadAsync(file);

    // 遍历ZIP内所有文件
    for (const [relativePath, fileEntry] of Object.entries(zip.files)) {
        try {
            // 处理JSON文件
            if (relativePath.endsWith(".json")) {
                const jsonText = await fileEntry.async("string");
                const jsonData = JSON.parse(jsonText);
                console.log("[JSZip] JSON 文件:", relativePath, jsonData);
            } 
            // 处理XML文件
            else if (relativePath.endsWith(".xml")) {
                const xmlText = await fileEntry.async("string");
                console.log("[JSZip] XML 文件:", relativePath, xmlText);
            }
            // 可以继续添加其他文件类型的处理逻辑
        } catch (err) {
            console.error(`[JSZip] 解析失败: ${relativePath}`, err);
        }
    }
}

3.2 使用 fflate 解压 ZIP 文件

fflate 的同步 API 和底层控制提供了更高的性能,但需要更多的手动处理。

import { unzipSync, strFromU8 } from 'fflate';

async function decompressWithFFlate(file: File) {
    // 将File对象转换为ArrayBuffer,然后转为Uint8Array
    const arrayBuffer = await file.arrayBuffer();
    const files = unzipSync(new Uint8Array(arrayBuffer));

    // 遍历解压后的文件
    for (const [path, fileEntry] of Object.entries(files)) {
        try {
            // 处理JSON文件
            if (path.endsWith(".json")) {
                const jsonText = strFromU8(fileEntry); // 将Uint8Array转换为字符串
                const jsonData = JSON.parse(jsonText);
                console.log("[FFlate] JSON 文件:", path, jsonData);
            } 
            // 处理XML文件
            else if (path.endsWith(".xml")) {
                const xmlText = strFromU8(fileEntry);
                console.log("[FFlate] XML 文件:", path, xmlText);
            }
        } catch (err) {
            console.error(`[FFlate] 解析失败: ${path}`, err);
        }
    }
}

3.3 使用 JSZip 创建 ZIP 文件

JSZip 提供了简单的接口来创建包含多个文件的 ZIP 压缩包。

import JSZip from 'jszip';

const jszip = new JSZip();

async function compressWithJSZip(files: FileList) {
    // 将FileList中的每个文件添加到ZIP中
    for (const file of files) {
        const arrayBuffer = await file.arrayBuffer();
        jszip.file(file.name, arrayBuffer); // 保留原始文件名
    }

    // 生成Blob格式的ZIP内容
    const content = await jszip.generateAsync({ type: "blob" });

    // 创建下载链接并触发下载
    const link = document.createElement("a");
    link.href = URL.createObjectURL(content);
    link.download = "archive-jszip.zip";
    link.click();
    
    // 释放URL对象
    URL.revokeObjectURL(link.href);
}

3.4 使用 fflate 创建 ZIP 文件

fflate 使用同步方式创建 ZIP 文件,效率更高但需要手动准备数据。

import { zipSync } from 'fflate';

async function compressWithFFlate(files: FileList) {
    const fileMap: Record<string, Uint8Array> = {};

    // 准备文件数据
    for (const file of files) {
        const arrayBuffer = await file.arrayBuffer();
        fileMap[file.name] = new Uint8Array(arrayBuffer);
    }

    // 同步生成ZIP二进制数据
    const zipped = zipSync(fileMap);

    // 创建Blob并下载
    const blob = new Blob([zipped], { type: "application/zip" });
    const link = document.createElement("a");
    link.href = URL.createObjectURL(blob);
    link.download = "archive-fflate.zip";
    link.click();
    
    // 释放URL对象
    URL.revokeObjectURL(link.href);
}

4. 完整示例与界面集成

以下是一个完整的示例,集成了图形界面,允许用户在 JSZip 和 fflate 之间切换,并选择压缩或解压模式:

import { GUI } from 'lil-gui';
import JSZip from 'jszip';
import { strFromU8, unzipSync, zipSync } from "fflate";

const jszip = new JSZip();

window.onload = () => {
    const params = {
        type: "jszip",
        mode: "decompress",
        upload: () => {
            params.mode === "decompress" ? fileInput.click() : uploadFile.click();
        }
    }
    
    // 创建图形控制界面
    const gui = new GUI();
    gui.add(params, 'mode', ['decompress', 'compress']);
    gui.add(params, 'type', ['fflate', 'jszip']);
    gui.add(params, 'upload');

    // 方法映射
    const decompressMap = {
        jszip: decompressWithJSZip,
        fflate: decompressWithFFlate
    }

    const compressMap = {
        jszip: compressWithJSZip,
        fflate: compressWithFFlate
    }

    // 解压文件输入
    const fileInput = document.createElement('input');
    fileInput.style.display = 'none';
    fileInput.type = 'file';
    fileInput.accept = '.zip';
    fileInput.multiple = true;
    document.body.appendChild(fileInput);

    fileInput.addEventListener('change', async (e) => {
        const files = (e.target as HTMLInputElement).files;
        if (!files) {
            return;
        }
        console.time(`use ${params.type}`);
        await Promise.all(Array.from(files).map(async (file) => {
            await decompressMap[params.type as keyof typeof decompressMap](file);
        }));
        console.timeEnd(`use ${params.type}`);
        (e.target as HTMLInputElement).value = "";
    })

    // 压缩文件输入
    const uploadFile = document.createElement('input');
    uploadFile.style.display = 'none';
    uploadFile.type = 'file';
    uploadFile.multiple = true;
    document.body.appendChild(uploadFile);

    uploadFile.addEventListener('change', async (e) => {
        const files = (e.target as HTMLInputElement).files;
        if (!files) {
            return;
        }
        await compressMap[params.type as keyof typeof compressMap](files);
        (e.target as HTMLInputElement).value = "";
    })
}

async function decompressWithJSZip(file: File) {
    const zip = await jszip.loadAsync(file);

    for (const [relativePath, fileEntry] of Object.entries(zip.files)) {
        try {
            if (relativePath.endsWith(".json")) {
                const jsonText = await fileEntry.async("string");
                const jsonData = JSON.parse(jsonText);
                console.log("[JSZip] JSON 文件:", relativePath, jsonData);
            } else if (relativePath.endsWith(".xml")) {
                const xmlText = await fileEntry.async("string");
                console.log("[JSZip] XML 文件:", relativePath, xmlText);
            }
        } catch (err) {
            console.error(`[JSZip] 解析失败: ${relativePath}`, err);
        }
    }
}

async function decompressWithFFlate(file: File) {
    const arrayBuffer = await file.arrayBuffer();
    const files = unzipSync(new Uint8Array(arrayBuffer));

    for (const [path, fileEntry] of Object.entries(files)) {
        try {
            if (path.endsWith(".json")) {
                const jsonText = strFromU8(fileEntry);
                const jsonData = JSON.parse(jsonText);
                console.log("[FFlate] JSON 文件:", path, jsonData);
            } else if (path.endsWith(".xml")) {
                const xmlText = strFromU8(fileEntry);
                console.log("[FFlate] XML 文件:", path, xmlText);
            }
        } catch (err) {
            console.error(`[FFlate] 解析失败: ${path}`, err);
        }
    }
}

async function compressWithJSZip(files: FileList) {
    // 添加每个文件
    for (const file of files) {
        const arrayBuffer = await file.arrayBuffer();
        jszip.file(file.name, arrayBuffer); // 保留原文件名
    }

    // 生成 Blob 格式的 zip
    const content = await jszip.generateAsync({ type: "blob" });

    // 下载
    const link = document.createElement("a");
    link.href = URL.createObjectURL(content);
    link.download = "archive-jszip.zip";
    link.click();
}

async function compressWithFFlate(files: FileList) {
    const fileMap: Record<string, Uint8Array> = {};

    for (const file of files) {
        const arrayBuffer = await file.arrayBuffer();
        fileMap[file.name] = new Uint8Array(arrayBuffer);
    }

    // 生成 zip(二进制)
    const zipped = zipSync(fileMap);

    // 下载
    const blob = new Blob([zipped], { type: "application/zip" });
    const link = document.createElement("a");
    link.href = URL.createObjectURL(blob);
    link.download = "archive-fflate.zip";
    link.click();
}

5. 性能优化与最佳实践

在前端处理 ZIP 文件时,性能优化尤为重要。

6. 总结与选择建议

根据不同的应用场景,我对库的选择有以下建议:

无论选择哪个库,前端处理 ZIP 文件的能力都为我们开辟了新的可能性,从优化资源加载到创建更丰富的文件交互体验,这些工具都是现代前端开发中值得掌握的利器。

到此这篇关于前端进行ZIP处理的方法实现与对比(JSZipvsfflate)的文章就介绍到这了,更多相关前端处理ZIP内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文