最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JavaScript實(shí)現(xiàn)文件夾壓縮與解壓縮完整攻略

 更新時間:2025年11月12日 14:29:02   作者:Fisch FLeisch  
本文詳細(xì)介紹了如何使用JavaScript在瀏覽器環(huán)境中實(shí)現(xiàn)文件和文件夾的壓縮與解壓縮,通過結(jié)合HTML5的FileAPI和BlobAPI,以及Zip.js和JSZip庫,可以完成從用戶選擇文件到生成ZIP文件的壓縮流程,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧

簡介:在瀏覽器環(huán)境中,JavaScript無法直接訪問文件系統(tǒng),但借助HTML5的File API和Blob API,結(jié)合Zip.js、JSZip等庫,可實(shí)現(xiàn)文件或文件夾的壓縮與解壓縮。本文詳細(xì)講解如何通過用戶文件選擇、讀取ArrayBuffer、創(chuàng)建Zip對象、添加文件、生成下載鏈接等步驟完成壓縮;以及如何加載ZIP文件、遍歷內(nèi)容、提取數(shù)據(jù)實(shí)現(xiàn)解壓。適用于前端文件處理場景,提升Web應(yīng)用交互能力。

1. JavaScript瀏覽器環(huán)境文件操作限制

JavaScript作為一種主要運(yùn)行于瀏覽器端的腳本語言,在早期設(shè)計(jì)中出于安全考慮對本地文件系統(tǒng)的訪問能力進(jìn)行了嚴(yán)格限制。在傳統(tǒng)的Web開發(fā)模型中,JavaScript無法直接讀取、寫入或遍歷用戶設(shè)備上的任意文件和目錄,這種沙箱機(jī)制有效防止了惡意代碼對用戶數(shù)據(jù)的非法訪問。

盡管如此,隨著HTML5標(biāo)準(zhǔn)的發(fā)展, File API 、 Blob API 等新型接口逐步提供了在用戶授權(quán)前提下進(jìn)行有限文件操作的能力。所有文件訪問必須由用戶主動觸發(fā)(如通過 <input type="file"> ),且操作范圍被限定在所選文件的臨時引用內(nèi),不能直接訪問路徑或持久化存儲。這一安全邊界確保了前端文件處理的安全性與可控性,也為后續(xù)API的合法使用奠定了基礎(chǔ)。

2. HTML5 File API與Blob API基礎(chǔ)應(yīng)用

隨著現(xiàn)代Web應(yīng)用對文件處理能力的需求不斷增長,瀏覽器環(huán)境下的前端代碼已不再局限于簡單的表單提交或圖片預(yù)覽。在文檔編輯器、離線歸檔系統(tǒng)、云盤客戶端等復(fù)雜場景中,開發(fā)者需要能夠直接操作用戶選擇的本地文件,并對其進(jìn)行讀取、轉(zhuǎn)換、壓縮甚至生成新文件。為此,HTML5引入了 File API Blob API 作為標(biāo)準(zhǔn)接口,使得JavaScript可以在安全沙箱內(nèi)以可控方式訪問用戶授權(quán)的文件內(nèi)容。

這兩套API構(gòu)成了現(xiàn)代瀏覽器中文件交互的核心基礎(chǔ)設(shè)施。它們并非賦予腳本任意訪問硬盤的權(quán)利,而是建立在“用戶主動觸發(fā)”和“上下文隔離”的原則之上——所有文件操作都必須源自用戶的明確行為(如點(diǎn)擊上傳按鈕),且只能處理由用戶顯式選擇的文件對象。這種設(shè)計(jì)既滿足了功能需求,又保障了安全性。

更重要的是,F(xiàn)ile API 與 Blob API 的結(jié)合為前端實(shí)現(xiàn)諸如大文件分片上傳、客戶端側(cè)圖像裁剪、PDF合并、視頻元數(shù)據(jù)提取等功能提供了底層支持。理解其核心對象、數(shù)據(jù)結(jié)構(gòu)以及異步處理機(jī)制,是構(gòu)建高性能、高可靠性的文件處理邏輯的前提。

2.1 File API的核心對象與使用場景

File API 是 HTML5 中用于處理用戶選擇文件的標(biāo)準(zhǔn)接口集合,它定義了一組關(guān)鍵對象,包括 File 、 FileList 和通過 <input type="file"> 觸發(fā)的文件選擇機(jī)制。這些組件共同構(gòu)成了一條從用戶界面到數(shù)據(jù)處理的完整通路,使開發(fā)者能夠在不依賴服務(wù)器的情況下獲取并解析本地文件的基本信息和原始字節(jié)流。

該API的設(shè)計(jì)哲學(xué)強(qiáng)調(diào) 用戶控制權(quán)優(yōu)先 。任何文件訪問都必須經(jīng)過用戶主動操作(例如點(diǎn)擊“選擇文件”按鈕),瀏覽器不會允許腳本靜默讀取設(shè)備上的任意路徑。這不僅符合隱私保護(hù)規(guī)范,也避免了潛在的安全風(fēng)險。與此同時,F(xiàn)ile API 提供了足夠豐富的元數(shù)據(jù)和訪問方法,足以支撐大多數(shù)前端文件處理任務(wù)。

為了深入掌握這一機(jī)制,我們需要逐一剖析其核心組成部分: File 對象如何封裝文件元信息與內(nèi)容引用; FileList 如何管理多選文件集合;以及如何通過標(biāo)準(zhǔn)HTML控件驅(qū)動整個流程。

2.1.1 File對象的獲取與屬性解析

File 對象是 File API 的基本單元,代表一個用戶選擇的文件實(shí)例。它是 Blob 的子類型,因此繼承了所有 Blob 的特性(如 .size .type 、 .slice() 方法),同時額外包含了文件名( .name )和最后修改時間( .lastModified )兩個關(guān)鍵屬性。

當(dāng)用戶通過 <input type="file"> 控件選擇文件后,瀏覽器會創(chuàng)建一個或多個 File 實(shí)例,并將其組織成 FileList 集合供腳本訪問。每一個 File 實(shí)例都包含以下主要屬性:

屬性名類型描述
name string文件的原始名稱(不含路徑,出于安全考慮路徑被屏蔽)
size number文件大小,單位為字節(jié)
type stringMIME 類型,如 image/jpeg 、 text/plain ,若無法識別則為空字符串
lastModified number最后修改時間的時間戳(毫秒)
webkitRelativePath string若從目錄選擇中獲取,表示相對于根目錄的路徑

下面是一個典型的 File 對象示例:

// 假設(shè)用戶選擇了名為 "report.pdf" 的文件
const file = document.querySelector('input[type="file"]').files[0];
console.log(file.name);           // "report.pdf"
console.log(file.size);           // 1048576 (1MB)
console.log(file.type);           // "application/pdf"
console.log(file.lastModified);   // 1700000000000
console.log(file.webkitRelativePath); // "" (普通文件選擇)

值得注意的是,盡管 File 繼承自 Blob ,但它并不立即加載文件內(nèi)容。它的作用更像是一個“句柄”或“引用”,指向?qū)嶋H存儲在用戶設(shè)備上的文件資源。真正的內(nèi)容讀取需借助 FileReader URL.createObjectURL() 等異步手段完成。

此外,由于瀏覽器出于安全考慮屏蔽了完整的文件路徑, file.name 只返回文件名本身。例如,即使用戶從 C:\Users\Alice\Documents\photo.png 選擇文件, name 仍僅為 "photo.png" 。這一點(diǎn)對于后續(xù)構(gòu)建歸檔結(jié)構(gòu)時尤為重要——若要保留目錄層級,必須依賴其他機(jī)制(如目錄選擇API)獲取相對路徑。

獲取File對象的技術(shù)路徑

最常見的方式是監(jiān)聽 <input type="file"> change 事件:

<input type="file" id="fileInput" multiple />
<script>
document.getElementById('fileInput').addEventListener('change', function(event) {
    const files = event.target.files; // FileList 對象
    for (let i = 0; i < files.length; i++) {
        const file = files[i];
        console.log(`文件 ${i + 1}:`, file.name, file.size, file.type);
    }
});
</script>

上述代碼展示了如何通過事件監(jiān)聽獲取 File 對象列表,并遍歷輸出每個文件的元數(shù)據(jù)。這是絕大多數(shù)文件上傳功能的起點(diǎn)。

?? 注意: event.target.files 是一個類數(shù)組對象( FileList ),不能使用數(shù)組原生方法(如 map 、 forEach ),需先轉(zhuǎn)換為數(shù)組:

javascript const fileArray = Array.from(event.target.files);

使用場景分析

File 對象廣泛應(yīng)用于以下場景:

  • 文件驗(yàn)證 :根據(jù) .type 判斷是否為允許類型(如僅接受 .png .pdf )。
  • 預(yù)覽生成 :利用 .type 匹配對應(yīng)的預(yù)覽方式(圖片用 <img> ,文本用 readAsText() )。
  • 上傳準(zhǔn)備 :將 .size 用于進(jìn)度估算, .name 用于服務(wù)端保存命名。
  • 日志記錄 :使用 .lastModified 進(jìn)行版本比對或緩存判斷。

綜上所述, File 不僅是文件的“身份證”,更是后續(xù)所有處理操作的數(shù)據(jù)源頭。準(zhǔn)確理解和使用其屬性,是構(gòu)建健壯文件系統(tǒng)的基石。

2.1.2 FileList對象在多文件輸入中的作用

當(dāng)用戶啟用 <input type="file" multiple> 或選擇整個文件夾時,瀏覽器將返回一個 FileList 對象,用于容納多個 File 實(shí)例。 FileList 雖然外觀類似數(shù)組,但本質(zhì)上是一個只讀的類數(shù)組集合,具備索引訪問和 .length 屬性,卻不提供 push 、 pop forEach 等數(shù)組方法。

其存在意義在于統(tǒng)一管理批量文件的選擇結(jié)果,尤其適用于需要同時處理多個文件的應(yīng)用場景,如批量圖片上傳、多文檔歸檔打包、視頻剪輯素材導(dǎo)入等。

結(jié)構(gòu)與訪問方式
const input = document.getElementById('multiFileInput');
input.addEventListener('change', () => {
    const fileList = input.files; // FileList 實(shí)例
    console.log(fileList.length); // 文件數(shù)量
    // 訪問單個文件
    for (let i = 0; i < fileList.length; i++) {
        const file = fileList[i];
        console.log(file.name);
    }
    // 轉(zhuǎn)換為真實(shí)數(shù)組以便使用現(xiàn)代方法
    const fileArray = Array.from(fileList);
    fileArray.forEach(f => console.log(f.name));
});

上面的例子演示了如何安全地遍歷 FileList 并提取每個 File 對象。注意不能直接調(diào)用 fileList.forEach() ,因?yàn)? FileList 沒有此方法。

實(shí)際應(yīng)用場景:限制文件數(shù)量與類型過濾

在實(shí)際開發(fā)中,常需對 FileList 進(jìn)行篩選和驗(yàn)證。例如,限制最多上傳5個圖片文件:

function validateFiles(fileList) {
    const maxFiles = 5;
    const allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
    if (fileList.length > maxFiles) {
        alert(`最多只能選擇 ${maxFiles} 個文件`);
        return false;
    }
    for (let file of fileList) {
        if (!allowedTypes.includes(file.type)) {
            alert(`${file.name} 不是允許的圖片格式`);
            return false;
        }
    }
    return true;
}

該函數(shù)可在 change 事件中調(diào)用,提前攔截非法輸入,提升用戶體驗(yàn)。

與DOM的聯(lián)動:動態(tài)展示選中文件

結(jié)合 DOM 操作,可實(shí)時展示用戶選擇的文件列表:

<input type="file" id="fileInput" multiple />
<ul id="fileList"></ul>
<script>
document.getElementById('fileInput').addEventListener('change', function(e) {
    const output = document.getElementById('fileList');
    output.innerHTML = ''; // 清空舊列表
    Array.from(e.target.files).forEach(file => {
        const li = document.createElement('li');
        li.textContent = `${file.name} (${(file.size / 1024).toFixed(2)} KB)`;
        output.appendChild(li);
    });
});
</script>

這種方式增強(qiáng)了交互透明度,讓用戶清楚看到自己選擇了哪些文件。

流程圖:FileList處理流程
graph TD
    A[用戶選擇多個文件] --> B{觸發(fā) input change 事件}
    B --> C[獲取 FileList 對象]
    C --> D[檢查文件數(shù)量是否超限]
    D -- 超限 --> E[提示錯誤并終止]
    D -- 正常 --> F[遍歷每個 File]
    F --> G[驗(yàn)證 MIME 類型]
    G -- 不合法 --> H[標(biāo)記并警告]
    G -- 合法 --> I[加入待處理隊(duì)列]
    I --> J[生成預(yù)覽或上傳]

此流程體現(xiàn)了從用戶輸入到業(yè)務(wù)處理的典型路徑, FileList 在其中扮演著承上啟下的角色。

2.1.3 通過input[type=”file”]實(shí)現(xiàn)用戶驅(qū)動的文件選擇

<input type="file"> 是觸發(fā) File API 操作的入口控件。它不僅是UI元素,更是權(quán)限請求的載體。只有通過此類控件的用戶交互,腳本才能合法獲得 File 對象。

其基本語法如下:

<input type="file" 
       id="uploader" 
       accept=".jpg,.png" 
       multiple 
       webkitdirectory />

各屬性含義如下:

屬性說明
accept 限定可選文件類型,支持 MIME 類型或擴(kuò)展名(如 .pdf
multiple 允許選擇多個文件
webkitdirectory (非標(biāo)準(zhǔn))允許選擇整個文件夾(Chrome/Firefox 支持)
示例:帶過濾的多文件選擇器
<label for="filePicker">請選擇圖像文件:</label>
<input type="file" 
       id="filePicker" 
       accept="image/*" 
       multiple />
<script>
document.getElementById('filePicker').addEventListener('change', async (e) => {
    const files = Array.from(e.target.files);
    const imageFiles = files.filter(f => f.type.startsWith('image/'));
    const previews = document.getElementById('previewContainer');
    previews.innerHTML = '';
    for (const file of imageFiles) {
        const img = new Image();
        img.src = URL.createObjectURL(file); // 創(chuàng)建臨時URL
        img.style.maxWidth = '200px';
        img.onload = () => URL.revokeObjectURL(img.src); // 釋放內(nèi)存
        previews.appendChild(img);
    }
});
</script>
<div id="previewContainer"></div>

該示例實(shí)現(xiàn)了圖片選擇后的即時預(yù)覽功能。關(guān)鍵點(diǎn)在于使用 URL.createObjectURL(file) File 轉(zhuǎn)換為可用于 <img src> 的地址,并在加載完成后調(diào)用 revokeObjectURL 釋放資源,防止內(nèi)存泄漏。

安全性與兼容性考量
  • 路徑隱藏 :無論操作系統(tǒng)路徑為何, file.name 始終不包含目錄信息。
  • MIME 類型不可信 file.type 由瀏覽器根據(jù)擴(kuò)展名推測,可能被篡改,不能作為唯一驗(yàn)證依據(jù)。
  • webkitdirectory 兼容性差 :Safari 和 Edge 不完全支持,生產(chǎn)環(huán)境需降級處理。

綜上, input[type="file"] 是連接用戶與前端文件系統(tǒng)的橋梁。合理配置屬性并配合 JavaScript 處理,可實(shí)現(xiàn)靈活而安全的文件導(dǎo)入機(jī)制。

3. 使用Zip.js進(jìn)行文件壓縮流程

在現(xiàn)代Web應(yīng)用中,前端直接處理文件歸檔的需求日益增長。尤其是在文檔管理、云存儲上傳前預(yù)處理、離線編輯器導(dǎo)出等場景下,將多個用戶選擇的文件打包為ZIP格式成為一種常見且必要的功能。傳統(tǒng)的做法依賴服務(wù)器端完成壓縮任務(wù),不僅增加了網(wǎng)絡(luò)開銷,也延長了響應(yīng)時間。隨著瀏覽器能力的不斷增強(qiáng),尤其是JavaScript異步處理能力和二進(jìn)制數(shù)據(jù)操作接口的成熟,已經(jīng)可以在客戶端實(shí)現(xiàn)完整的ZIP文件生成邏輯。

Zip.js 是一個輕量級、模塊化、基于Promise的JavaScript庫,專為瀏覽器環(huán)境設(shè)計(jì),支持流式讀寫ZIP文件,能夠高效地執(zhí)行壓縮與解壓操作而無需依賴后端服務(wù)。它充分利用了HTML5中的Blob API、FileReader API以及Web Workers機(jī)制,在保證性能的同時提供了靈活的配置選項(xiàng)。相比其他同類工具如JSZip,Zip.js 更加注重內(nèi)存效率和可擴(kuò)展性,特別適合處理大體積或多層級結(jié)構(gòu)的文件集合。

本章節(jié)將深入剖析如何利用 Zip.js 實(shí)現(xiàn)從用戶選擇文件到最終生成并下載ZIP包的完整流程。我們將逐步解析其核心組件的工作原理,結(jié)合實(shí)際代碼演示每一步的技術(shù)細(xì)節(jié),并通過流程圖與參數(shù)表格幫助理解內(nèi)部機(jī)制。整個過程不僅涉及API調(diào)用,還包括對ZIP文件結(jié)構(gòu)的理解、路徑層級的維護(hù)、MIME類型的正確設(shè)置,以及最終Blob資源的安全發(fā)布方式。

3.1 Zip.js庫的引入與初始化配置

要在項(xiàng)目中使用 Zip.js,首先必須正確引入該庫。目前 Zip.js 提供了多種集成方式,開發(fā)者可根據(jù)項(xiàng)目構(gòu)建體系選擇最合適的方法。無論采用哪種方式,目標(biāo)都是確保 zip.js 的核心模塊(如 zip.Writer 、 zip.Reader )能夠在運(yùn)行時被正確加載和實(shí)例化。

3.1.1 通過CDN或npm安裝引入Zip.js

最簡單的引入方式是通過CDN直接在HTML頁面中加載:

<script src="https://cdn.jsdelivr.net/npm/@zip.js/zip.js@2.7.54/dist/zip.min.js"></script>

此方法適用于快速原型開發(fā)或靜態(tài)網(wǎng)站,無需任何構(gòu)建工具即可啟用Zip.js的所有功能。腳本加載完成后,全局對象 zip 即可用,例如可以通過 zip.FileWriter zip.createWriter 等方式訪問API。

對于使用現(xiàn)代前端框架(如React、Vue、Angular)的項(xiàng)目,推薦通過npm安裝以獲得更好的模塊管理和Tree-shaking優(yōu)化:

npm install @zip.js/zip.js

安裝完成后,在JavaScript文件中按需導(dǎo)入所需模塊:

import { createWriter, BlobWriter, BlobReader } from '@zip.js/zip.js';

這種方式允許開發(fā)者僅引入需要的功能,減少最終打包體積。同時支持ES6+語法特性,便于與其他異步流程(如async/await)結(jié)合使用。

引入方式適用場景優(yōu)點(diǎn)缺點(diǎn)
CDN 直接引用快速驗(yàn)證、靜態(tài)頁面無需構(gòu)建,即插即用不利于版本控制,增加首屏加載延遲
npm 安裝 + 模塊導(dǎo)入SPA、工程化項(xiàng)目支持Tree-shaking,易于維護(hù)需要構(gòu)建系統(tǒng)支持

注意 :Zip.js 內(nèi)部依賴 Web Streams API Text Encoding API ,在部分舊版瀏覽器(如IE)中可能無法運(yùn)行。建議在生產(chǎn)環(huán)境中添加polyfill支持或進(jìn)行特性檢測。

3.1.2 初始化Writer對象以準(zhǔn)備寫入ZIP結(jié)構(gòu)

一旦Zip.js成功加載,下一步就是創(chuàng)建一個寫入器(Writer),用于構(gòu)造ZIP歸檔文件。Zip.js 提供了多種Writer類型,其中最常用的是 BlobWriter ,它可以將壓縮結(jié)果寫入一個Blob對象,便于后續(xù)生成下載鏈接。

以下是初始化Writer的典型代碼示例:

async function initZipWriter() {
  const blob = new Blob([], { type: 'application/zip' });
  const writer = await zip.createWriter(new zip.BlobWriter(blob));
  return writer;
}
代碼邏輯逐行解讀:
  • const blob = new Blob([], { type: 'application/zip' });
  • 創(chuàng)建一個空的Blob對象,指定MIME類型為 application/zip ,這是標(biāo)準(zhǔn)ZIP文件的媒體類型,有助于瀏覽器識別和處理。
  • await zip.createWriter(new zip.BlobWriter(blob));
  • 調(diào)用 createWriter 工廠函數(shù),傳入 BlobWriter 實(shí)例作為參數(shù)。 createWriter 返回一個Promise,解析后得到可用于添加文件的 Writer 對象。
  • return writer;
  • 將初始化完成的Writer返回,供后續(xù)調(diào)用 add() 方法使用。
graph TD
    A[開始] --> B{判斷引入方式}
    B -->|CDN| C[全局zip對象可用]
    B -->|npm| D[模塊導(dǎo)入特定API]
    C --> E[創(chuàng)建BlobWriter]
    D --> E
    E --> F[調(diào)用createWriter]
    F --> G[獲取Writer實(shí)例]
    G --> H[準(zhǔn)備添加文件]

該流程清晰展示了從庫加載到Writer初始化的全過程。值得注意的是, createWriter 是異步操作,必須使用 await .then() 處理Promise結(jié)果。這是因?yàn)榈讓涌赡苌婕癢orker線程的啟動或緩沖區(qū)的異步分配。

此外,Zip.js 還支持其他Writer類型,如 Uint8ArrayWriter (將結(jié)果寫入字節(jié)數(shù)組)和 HttpWriter (直接上傳至服務(wù)器),但 BlobWriter 是前端本地壓縮最常用的方案。

3.2 壓縮流程的理論模型

要高效使用 Zip.js,不能僅僅停留在API調(diào)用層面,還需理解ZIP文件本身的組織結(jié)構(gòu)及其在瀏覽器中的流式處理機(jī)制。Zip.js 的設(shè)計(jì)理念正是基于“邊生成邊寫入”的流模型,避免一次性將所有數(shù)據(jù)加載進(jìn)內(nèi)存,從而提升大文件處理能力。

3.2.1 ZIP文件格式的基本組成結(jié)構(gòu)(中央目錄、本地文件頭等)

ZIP是一種廣泛使用的歸檔格式,其結(jié)構(gòu)由多個關(guān)鍵部分構(gòu)成:

  • 本地文件頭(Local File Header)
    • 每個被壓縮的文件前都有一個本地文件頭,記錄該文件的元信息,包括文件名、壓縮方法、未壓縮大小、CRC校驗(yàn)值、最后修改時間等。
  • 文件數(shù)據(jù)(File Data)
    • 緊隨本地文件頭之后的是經(jīng)過壓縮的實(shí)際內(nèi)容(若未壓縮則為原始數(shù)據(jù))。
  • 數(shù)據(jù)描述符(Data Descriptor) (可選)
    • 當(dāng)采用流式寫入且事先不知道壓縮后大小時,可在文件數(shù)據(jù)后附加數(shù)據(jù)描述符,包含CRC和壓縮大小。
  • 中央目錄(Central Directory)
    • 位于ZIP文件末尾,集中存儲所有文件條目的元信息副本。它是解壓工具定位文件的主要依據(jù)。
  • 結(jié)尾記錄(End of Central Directory Record)
    • 標(biāo)記ZIP文件結(jié)束,包含中央目錄的位置、數(shù)量及注釋信息。
[Local Header 1][File Data 1][Data Descriptor?]
[Local Header 2][File Data 2][Data Descriptor?]
[Central Directory]
[End of Central Directory Record]

Zip.js 在調(diào)用 add() 方法時會立即寫入本地文件頭和數(shù)據(jù),而在調(diào)用 finalize() 時才生成中央目錄和結(jié)尾記錄。這種分階段寫入策略使得即使面對大量文件也能保持較低內(nèi)存占用。

3.2.2 流式寫入與內(nèi)存緩沖區(qū)管理策略

Zip.js 默認(rèn)使用 Web Workers 進(jìn)行后臺壓縮計(jì)算,主界面線程不會被阻塞。每個添加的文件都會通過 TransformStream 流水線進(jìn)行處理:

flowchart LR
    File --> FileReader --> TransformStream --> BlobWriter

具體流程如下:
- 使用 FileReader 將原始文件讀取為 ArrayBuffer
- 將數(shù)據(jù)送入壓縮流(如Deflate)
- 壓縮后的片段逐步寫入 BlobWriter 的內(nèi)部緩沖區(qū)
- 緩沖區(qū)達(dá)到一定閾值時自動 flush 到目標(biāo) Blob

這一機(jī)制的關(guān)鍵優(yōu)勢在于: 不需要等待所有文件讀取完畢再開始壓縮 ,而是可以并發(fā)處理多個文件,顯著提升整體性能。

此外,Zip.js 允許配置 bufferSize 參數(shù)來控制每次讀取的塊大?。J(rèn)為64KB),開發(fā)者可根據(jù)設(shè)備性能調(diào)整該值以平衡速度與內(nèi)存消耗。

參數(shù)類型默認(rèn)值說明
bufferSize number65536每次讀取的字節(jié)塊大小
level number6壓縮級別(0~9,0為無壓縮)
useWebWorkers booleantrue是否啟用Worker線程

這些參數(shù)可通過 createWriter 的第三個選項(xiàng)參數(shù)傳入:

const writer = await zip.createWriter(
  new zip.BlobWriter('application/zip'),
  { level: 6 },
  () => new zip.Deflate()
);

上述代碼顯式指定了壓縮算法為Deflate,并設(shè)置了壓縮級別。這對于控制輸出文件大小非常有用。

3.3 具體實(shí)現(xiàn)步驟

掌握了理論基礎(chǔ)之后,接下來進(jìn)入實(shí)戰(zhàn)環(huán)節(jié)。我們將展示如何一步步使用 Zip.js 將多個用戶選擇的文件打包成ZIP。

3.3.1 創(chuàng)建zip.Writer實(shí)例并指定MIME類型

如前所述,首先要創(chuàng)建一個支持ZIP輸出的Writer:

const blobWriter = new zip.BlobWriter('application/zip');
const writer = await zip.createWriter(blobWriter);

此處明確指定MIME類型為 'application/zip' ,確保生成的Blob能被瀏覽器正確識別為ZIP文件。

3.3.2 調(diào)用add()方法逐個添加文件及其路徑信息

add() 方法是Zip.js的核心接口之一,用于向歸檔中添加單個條目:

await writer.add('documents/report.txt', new zip.TextReader('Hello World'));

該方法接受三個主要參數(shù):

參數(shù)類型描述
name string文件在ZIP中的路徑(支持子目錄)
reader Reader instance提供文件內(nèi)容的數(shù)據(jù)源
options object可選配置,如進(jìn)度回調(diào)、壓縮級別等

常見的Reader類型包括:

  • TextReader :用于字符串內(nèi)容
  • BlobReader :用于Blob或File對象
  • Uint8ArrayReader :用于二進(jìn)制數(shù)組

例如,添加一個用戶選擇的圖片文件:

const file = input.files[0]; // 來自<input type="file">
await writer.add(file.name, new zip.BlobReader(file));

3.3.3 處理子文件夾層級關(guān)系以保留原始目錄結(jié)構(gòu)

ZIP支持嵌套目錄結(jié)構(gòu)。只需在 name 參數(shù)中使用斜杠 / 分隔路徑即可:

await writer.add('photos/vacation/sunset.jpg', photoReader);

這將在ZIP中創(chuàng)建 photos/vacation/ 目錄,并將文件放入其中。注意:Zip.js 不會自動創(chuàng)建中間目錄條目,但大多數(shù)解壓工具仍能正確重建目錄樹。

若需手動添加目錄項(xiàng)(如空文件夾),可使用空Blob:

await writer.add('empty-folder/', new zip.BlobReader(new Blob()));

路徑以 / 結(jié)尾表示這是一個目錄。

3.4 輸出與下載最終ZIP文件

當(dāng)所有文件添加完畢后,必須調(diào)用 finalize() 方法完成歸檔封包。

3.4.1 調(diào)用finalize()完成歸檔封包

await writer.close();

調(diào)用 close() (等價于 finalize() )會觸發(fā)以下動作:
- 寫入中央目錄
- 更新結(jié)尾記錄
- 關(guān)閉所有流通道
- 返回最終的Blob對象

const zipBlob = await blobWriter.getData();

getData() 獲取最終生成的Blob。

3.4.2 獲取生成的Blob對象并通過URL.createObjectURL發(fā)布

const url = URL.createObjectURL(zipBlob);

該URL指向內(nèi)存中的Blob資源,有效期直到調(diào)用 revokeObjectURL

3.4.3 利用a標(biāo)簽的download屬性實(shí)現(xiàn)自動下載

const a = document.createElement('a');
a.href = url;
a.download = 'archive.zip';
a.click();
URL.revokeObjectURL(url); // 清理內(nèi)存

這段代碼模擬點(diǎn)擊行為,觸發(fā)瀏覽器下載對話框。

完整示例封裝如下:

async function compressFiles(files) {
  const blobWriter = new zip.BlobWriter('application/zip');
  const writer = await zip.createWriter(blobWriter);
  for (const file of files) {
    await writer.add(file.name, new zip.BlobReader(file));
  }
  await writer.close();
  const zipBlob = await blobWriter.getData();
  const url = URL.createObjectURL(zipBlob);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'selected-files.zip';
  a.click();
  URL.revokeObjectURL(url);
}

該函數(shù)接收 FileList File[] 數(shù)組,執(zhí)行全流程壓縮并觸發(fā)下載,適用于真實(shí)項(xiàng)目集成。

4. 使用Zip.js進(jìn)行文件解壓縮流程

前端文件處理能力的演進(jìn)不僅體現(xiàn)在壓縮技術(shù)上,更在于對已有歸檔數(shù)據(jù)的解析與還原。隨著Web應(yīng)用逐步承擔(dān)起傳統(tǒng)桌面軟件的部分職責(zé),如在線文檔編輯器、云存儲管理界面等場景中,用戶經(jīng)常需要上傳ZIP壓縮包并查看其內(nèi)部內(nèi)容。這就要求瀏覽器能夠在不依賴后端服務(wù)的前提下完成完整的解壓流程。 Zip.js 作為一款輕量級且功能完備的純JavaScript庫,支持在瀏覽器環(huán)境中實(shí)現(xiàn)ZIP文件的讀取與解壓縮操作。該過程涉及多個關(guān)鍵環(huán)節(jié):從原始二進(jìn)制數(shù)據(jù)的加載,到壓縮條目的遍歷提取,再到最終的數(shù)據(jù)轉(zhuǎn)換和DOM展示,每一步都需要精確控制以確保兼容性與用戶體驗(yàn)。

本章將深入剖析如何基于 Zip.js 構(gòu)建一個健壯的前端解壓系統(tǒng)。我們將圍繞數(shù)據(jù)準(zhǔn)備、結(jié)構(gòu)解析、內(nèi)容重建以及錯誤處理四大核心模塊展開討論,并結(jié)合實(shí)際代碼示例說明各階段的技術(shù)細(xì)節(jié)。通過這一完整流程的學(xué)習(xí),開發(fā)者可以掌握如何安全地加載用戶上傳的ZIP文件,高效地提取其中資源,并以直觀方式呈現(xiàn)給終端用戶。

4.1 解壓前的數(shù)據(jù)準(zhǔn)備與驗(yàn)證

在執(zhí)行任何解壓邏輯之前,必須首先獲取用戶選擇的ZIP文件,并將其轉(zhuǎn)換為適合處理的二進(jìn)制格式。由于瀏覽器環(huán)境無法直接訪問本地文件系統(tǒng),所有輸入都需通過用戶主動觸發(fā)(例如點(diǎn)擊 <input type="file"> ),這是保障安全性的基礎(chǔ)機(jī)制。隨后,使用 FileReader 將選中的文件讀取為 ArrayBuffer ,以便后續(xù)由 zip.Reader 進(jìn)行解析。

4.1.1 讀取用戶上傳的ZIP文件為ArrayBuffer

要開始解壓流程,第一步是讓用戶選擇一個ZIP文件。HTML提供了標(biāo)準(zhǔn)的文件輸入控件:

<input type="file" id="zipFileInput" accept=".zip" />

設(shè)置 accept=".zip" 可提示用戶僅選擇ZIP格式文件,盡管這不能完全阻止非法文件上傳,但有助于提升交互體驗(yàn)。接下來,在JavaScript中監(jiān)聽文件變化事件:

document.getElementById('zipFileInput').addEventListener('change', async (event) => {
    const file = event.target.files[0];
    if (!file) return;
    // 使用FileReader讀取為ArrayBuffer
    const reader = new FileReader();
    reader.onload = function(e) {
        const arrayBuffer = e.target.result;
        handleZipFile(arrayBuffer); // 進(jìn)入下一步處理
    };
    reader.onerror = () => console.error("文件讀取失敗");
    reader.readAsArrayBuffer(file);
});

代碼邏輯逐行解讀:

  • 第2行:綁定 change 事件,當(dāng)用戶選擇文件時觸發(fā)。
  • 第3行:獲取第一個選中的文件對象( File 類型)。
  • 第6–11行:創(chuàng)建 FileReader 實(shí)例,設(shè)定 onload 回調(diào)函數(shù)用于接收讀取結(jié)果。
  • 第8行: e.target.result 即為文件的 ArrayBuffer 表示,包含原始字節(jié)流。
  • 第9行:調(diào)用自定義函數(shù) handleZipFile() 開始解壓主流程。
  • 第10行:監(jiān)聽錯誤事件,防止因文件損壞或權(quán)限問題導(dǎo)致靜默失敗。
  • 第11行:調(diào)用 readAsArrayBuffer() 啟動異步讀取,適用于二進(jìn)制數(shù)據(jù)。

此方法保證了文件內(nèi)容以原始字節(jié)形式加載,避免編碼轉(zhuǎn)換帶來的信息丟失,特別適合處理非文本類型的壓縮包。

4.1.2 使用zip.Reader加載壓縮包內(nèi)容

一旦獲得 ArrayBuffer ,即可使用 Zip.js 提供的 zip.Reader 接口初始化讀取器。該對象負(fù)責(zé)解析ZIP文件的內(nèi)部結(jié)構(gòu),包括中央目錄、本地文件頭等元數(shù)據(jù)。

假設(shè)已正確引入 Zip.js 庫(可通過 CDN 或模塊化導(dǎo)入),可如下初始化:

import * as zip from 'https://cdn.jsdelivr.net/npm/@zip.js/zip.js';
async function handleZipFile(arrayBuffer) {
    try {
        const uint8Array = new Uint8Array(arrayBuffer);
        const reader = new zip.Uint8ArrayReader(uint8Array);
        const zipReader = new zip.ZipReader(reader);
        const entries = await zipReader.getEntries();
        processEntries(entries, zipReader); // 繼續(xù)處理?xiàng)l目
    } catch (err) {
        console.error("解壓初始化失敗:", err);
    }
}

代碼邏輯逐行解讀:

  • 第1行:動態(tài)導(dǎo)入最新版本的 @zip.js/zip.js 模塊(現(xiàn)代瀏覽器支持 ES Module 形式)。
  • 第5行:將 ArrayBuffer 轉(zhuǎn)換為 Uint8Array ,這是 zip.Reader 所需的輸入類型。
  • 第6行:構(gòu)造 Uint8ArrayReader ,封裝底層數(shù)據(jù)流。
  • 第7行:創(chuàng)建 ZipReader 實(shí)例,啟動對ZIP結(jié)構(gòu)的解析。
  • 第9行:調(diào)用 getEntries() 異步獲取所有壓縮項(xiàng)列表,返回 Promise。
  • 第11行:傳入條目數(shù)組和 zipReader 實(shí)例進(jìn)入下一階段處理。
  • 第13–15行:捕獲可能拋出的異常,如格式錯誤、CRC校驗(yàn)失敗等。

ZipReader 在初始化過程中會自動驗(yàn)證ZIP文件的基本結(jié)構(gòu)完整性,若文件非標(biāo)準(zhǔn)ZIP格式,則 getEntries() 將拒絕并拋出異常。

4.1.3 檢查文件完整性與MIME類型安全性

盡管前端無法像服務(wù)器那樣進(jìn)行全面的內(nèi)容掃描,但仍可通過多種手段增強(qiáng)安全性與魯棒性。

驗(yàn)證項(xiàng)方法目的
文件擴(kuò)展名file.name.endsWith('.zip') 初步過濾明顯非ZIP文件
MIME類型file.type === 'application/zip' 利用瀏覽器識別的媒體類型做判斷
魔數(shù)校驗(yàn)(Magic Number)檢查前4字節(jié)是否為 PK\003\004 確認(rèn)真實(shí)ZIP格式
大小限制file.size < MAX_SIZE 防止大文件拖慢頁面

以下是綜合驗(yàn)證邏輯示例:

function isValidZipFile(file) {
    const MAX_SIZE = 50 * 1024 * 1024; // 50MB限制
    if (!file.name.toLowerCase().endsWith('.zip')) {
        alert("請上傳ZIP格式文件");
        return false;
    }
    if (!file.type || !file.type.includes('zip')) {
        alert("MIME類型不匹配,請確認(rèn)為有效ZIP文件");
        return false;
    }
    if (file.size > MAX_SIZE) {
        alert(`文件過大(${(file.size / 1024 / 1024).toFixed(2)}MB),超過50MB限制`);
        return false;
    }
    return true;
}

此外,還可以進(jìn)一步檢查 ArrayBuffer 的魔數(shù):

function checkMagicNumber(arrayBuffer) {
    const view = new Uint8Array(arrayBuffer, 0, 4);
    const magic = String.fromCharCode(view[0], view[1], view[2], view[3]);
    return magic === "PK\003\004"; // ZIP本地文件頭標(biāo)志
}

參數(shù)說明:

  • PK\003\004 是ZIP文件每個成員的本地文件頭起始標(biāo)識,幾乎所有合法ZIP均以此開頭。
  • 若檢測失敗,極可能是偽裝文件或已損壞,應(yīng)終止處理。
安全邊界設(shè)計(jì)建議
graph TD
    A[用戶選擇文件] --> B{是否為.zip?}
    B -- 否 --> C[提示錯誤并拒絕]
    B -- 是 --> D{MIME類型匹配?}
    D -- 否 --> C
    D -- 是 --> E{大小 ≤ 50MB?}
    E -- 否 --> F[提示超限]
    E -- 是 --> G[讀取ArrayBuffer]
    G --> H{魔數(shù)為PK\003\004?}
    H -- 否 --> I[判定為非法]
    H -- 是 --> J[允許進(jìn)入解壓流程]

該流程圖清晰展示了多層驗(yàn)證機(jī)制的執(zhí)行路徑,體現(xiàn)了“縱深防御”原則。即使某一層被繞過(如修改擴(kuò)展名),后續(xù)校驗(yàn)仍可攔截風(fēng)險。

綜上所述,解壓前的數(shù)據(jù)準(zhǔn)備不僅是技術(shù)起點(diǎn),更是安全防線的第一道關(guān)卡。只有經(jīng)過嚴(yán)格驗(yàn)證的文件才能進(jìn)入后續(xù)解析階段,從而保障系統(tǒng)的穩(wěn)定與用戶數(shù)據(jù)的安全。

4.2 文件條目遍歷與內(nèi)容提取

成功加載ZIP文件后,下一步是遍歷其中的所有條目(entries),這些條目代表壓縮包內(nèi)的各個文件或目錄。 Zip.js 提供了清晰的接口來訪問這些元數(shù)據(jù),并支持按需提取具體內(nèi)容。本節(jié)將詳細(xì)介紹如何高效地枚舉條目、解析屬性,并重建原始的目錄層級結(jié)構(gòu)。

4.2.1 enumerate()方法遍歷所有壓縮項(xiàng)

ZipReader getEntries() 方法返回一個包含所有條目的數(shù)組,每個條目是一個 Entry 對象,具備名稱、大小、壓縮方式、時間戳等豐富信息。

async function processEntries(entries, zipReader) {
    for (const entry of entries) {
        console.log(`文件名: ${entry.filename}`);
        console.log(`大小: ${entry.uncompressedSize} 字節(jié)`);
        console.log(`是否為目錄: ${entry.directory}`);
        console.log(`壓縮方法: ${entry.compressionMethod}`);
        if (!entry.directory) {
            const content = await entry.getData(new zip.BlobWriter());
            displayFileContent(entry.filename, content);
        }
    }
    await zipReader.close(); // 必須顯式關(guān)閉讀取器
}

代碼邏輯逐行解讀:

  • 第2行:使用 for...of 循環(huán)同步遍歷條目數(shù)組(雖然 getData 是異步的)。
  • 第4–7行:輸出條目的基本信息,可用于調(diào)試或預(yù)覽。
  • 第9–11行:僅對非目錄條目調(diào)用 getData() 獲取解壓后內(nèi)容。
  • 第10行: BlobWriter 表示希望將解壓結(jié)果寫成 Blob 對象,便于后續(xù)處理。
  • 第12行:調(diào)用自定義函數(shù)顯示內(nèi)容。
  • 第15行: 重要! 必須調(diào)用 close() 釋放內(nèi)存資源,否則可能導(dǎo)致內(nèi)存泄漏。

該方法適用于中小型ZIP包;對于大型文件,建議采用分批處理或流式讀取策略。

4.2.2 獲取每個條目的名稱、大小及是否為目錄

Entry 對象提供的關(guān)鍵屬性如下表所示:

屬性名類型說明
filename string文件路徑(含相對路徑,使用 / 分隔)
uncompressedSize number解壓后大?。ㄗ止?jié))
compressedSize number壓縮后大小
directory boolean是否為目錄條目
date Date最后修改時間
comment string條目注釋(可選)
encryption string加密方式(如無則為空)

特別注意: filename 中可能包含多級路徑,如 docs/report.txt images/thumbs/ ,需據(jù)此重建文件夾結(jié)構(gòu)。

4.2.3 根據(jù)路徑信息重建文件夾層級結(jié)構(gòu)

為了準(zhǔn)確還原原始目錄樹,我們需要根據(jù) filename 字段拆分路徑并生成對應(yīng)的虛擬目錄結(jié)構(gòu)。以下是一個構(gòu)建樹形結(jié)構(gòu)的實(shí)用函數(shù):

function buildDirectoryTree(entries) {
    const root = { name: '/', type: 'folder', children: [] };
    for (const entry of entries) {
        const pathParts = entry.filename.split('/').filter(p => p !== '');
        let current = root;
        for (let i = 0; i < pathParts.length; i++) {
            const part = pathParts[i];
            const isLast = i === pathParts.length - 1;
            let child = current.children.find(c => c.name === part);
            if (!child) {
                child = {
                    name: part,
                    type: isLast && !entry.directory ? 'file' : 'folder',
                    size: isLast ? entry.uncompressedSize : undefined,
                    entry: isLast ? entry : undefined
                };
                current.children.push(child);
            }
            current = child;
        }
    }
    return root;
}

參數(shù)說明:

  • 輸入: entries 數(shù)組,來自 getEntries() 。
  • 輸出:嵌套對象表示的文件樹,可用于遞歸渲染。
  • pathParts 拆分路徑,忽略空字符串(如結(jié)尾斜杠導(dǎo)致的問題)。
  • current 指針逐層下探,構(gòu)建父子關(guān)系。
  • isLast 判斷當(dāng)前部分是否為葉子節(jié)點(diǎn),決定類型為文件還是中間目錄。

該結(jié)構(gòu)可用于后續(xù)DOM渲染或?qū)С鰹镴SON格式供其他組件使用。

4.3 數(shù)據(jù)轉(zhuǎn)換與前端展示

解壓后的數(shù)據(jù)通常以 Blob String 形式存在,需根據(jù)不同文件類型進(jìn)行適配處理,以便在頁面中直接預(yù)覽或提供下載鏈接。

4.3.1 將解壓出的文件內(nèi)容轉(zhuǎn)為Blob或Text

getData() 支持多種寫入器類型,可根據(jù)需求靈活選擇:

// 獲取為文本(適用于 .txt, .json, .html 等)
const text = await entry.getData(new zip.TextWriter());
// 獲取為 Blob(通用)
const blob = await entry.getData(new zip.BlobWriter());
// 獲取為 ArrayBuffer(底層操作)
const buffer = await entry.getData(new zip.Uint8ArrayWriter());

擴(kuò)展說明:

  • TextWriter 自動處理字符編碼(默認(rèn)UTF-8),適合文本類文件。
  • BlobWriter 返回 Blob 對象,可用于生成預(yù)覽URL。
  • Uint8ArrayWriter 提供最原始的字節(jié)數(shù)組,常用于圖像或二進(jìn)制分析。

4.3.2 利用DataURL嵌入圖片或預(yù)覽文本文件

對于圖片文件(如 .png , .jpg ),可將其轉(zhuǎn)為 Data URL 并插入 <img> 標(biāo)簽:

function displayImage(filename, blob) {
    const url = URL.createObjectURL(blob);
    const img = document.createElement('img');
    img.src = url;
    img.alt = filename;
    img.style.maxWidth = '300px';
    document.body.appendChild(img);
}

對于文本文件,直接插入 <pre> 元素:

function displayText(filename, text) {
    const pre = document.createElement('pre');
    pre.textContent = text;
    pre.style.backgroundColor = '#f4f4f4';
    pre.style.padding = '10px';
    document.body.appendChild(pre);
}

4.3.3 動態(tài)生成DOM元素展示解壓結(jié)果列表

結(jié)合前面構(gòu)建的文件樹,可遞歸生成HTML結(jié)構(gòu):

function renderTree(node, parentEl) {
    const li = document.createElement('li');
    li.textContent = node.name + (node.type === 'file' ? ` (${node.size}B)` : '');
    if (node.children && node.children.length > 0) {
        const ul = document.createElement('ul');
        node.children.forEach(child => renderTree(child, ul));
        li.appendChild(ul);
    } else if (node.type === 'file') {
        li.onclick = () => openFile(node.entry); // 點(diǎn)擊打開文件
    }
    parentEl.appendChild(li);
}
flowchart TB
    Start[開始渲染文件樹] --> Check{是否有子節(jié)點(diǎn)?}
    Check -- 是 --> CreateUL[創(chuàng)建<ul>容器]
    CreateUL --> Loop[遍歷每個子節(jié)點(diǎn)]
    Loop --> RenderChild[遞歸調(diào)用renderTree]
    RenderChild --> AppendToUL[添加至<ul>]
    AppendToUL --> End
    Check -- 否 --> IsFile{是否為文件?}
    IsFile -- 是 --> AddClick[綁定點(diǎn)擊事件]
    AddClick --> End
    IsFile -- 否 --> End

此流程確保了目錄結(jié)構(gòu)的可視化表達(dá),提升了用戶體驗(yàn)。

4.4 錯誤處理與用戶體驗(yàn)優(yōu)化

4.4.1 捕獲解壓過程中可能出現(xiàn)的編碼異常

ZIP文件可能使用非UTF-8編碼命名(如GBK、Shift-JIS),導(dǎo)致 filename 出現(xiàn)亂碼。 Zip.js 支持指定解碼器:

const zipReader = new zip.ZipReader(reader, {
    decodeFileName: (bytes) => new TextDecoder('gbk').decode(bytes)
});

此外,還需捕獲 getData() 中可能發(fā)生的 CRC 校驗(yàn)失敗、解壓算法不支持等問題:

try {
    const content = await entry.getData(writer);
} catch (e) {
    console.warn(`提取 ${entry.filename} 失敗:`, e.message);
    showErrorToast(`無法解壓 ${entry.filename}`);
}

4.4.2 提供進(jìn)度條反饋提升交互感知

對于大文件,可通過監(jiān)聽 onprogress 事件實(shí)現(xiàn)進(jìn)度追蹤:

const zipReader = new zip.ZipReader(reader, {
    onprogress: (index, total) => {
        const percent = Math.round((index / total) * 100);
        updateProgressBar(percent);
    }
});

配合CSS樣式:

.progress-bar {
    width: 100%;
    background: #eee;
    height: 20px;
    border-radius: 10px;
    overflow: hidden;
}
.progress-fill {
    width: var(--width);
    background: #4CAF50;
    height: 100%;
    transition: width 0.3s ease;
}

實(shí)時反饋顯著改善用戶等待體驗(yàn),減少誤操作。

5. JSZip庫基本使用方法

隨著前端工程能力的不斷演進(jìn),瀏覽器端對文件處理的需求已從簡單的單文件讀取發(fā)展到復(fù)雜的歸檔操作。在這一背景下, JSZip 成為了開發(fā)者構(gòu)建 Web 端壓縮功能時最受歡迎的第三方庫之一。相較于原生 API 或其他輕量級實(shí)現(xiàn)(如 Zip.js),JSZip 提供了更高級、語義清晰且易于集成的接口設(shè)計(jì),尤其適合需要快速構(gòu)建 ZIP 打包與解包邏輯的應(yīng)用場景。本章深入剖析 JSZip 的核心用法,涵蓋其初始化流程、壓縮與解壓操作、多格式支持機(jī)制,并探討其在真實(shí)項(xiàng)目中的高級配置策略。

5.1 JSZip與Zip.js的功能對比分析

面對多種前端壓縮庫的選擇,開發(fā)者常需在性能、易用性和功能完整性之間權(quán)衡。JSZip 與 Zip.js 是目前主流的兩個 JavaScript 壓縮解決方案,它們均基于瀏覽器環(huán)境運(yùn)行,但設(shè)計(jì)理念和底層架構(gòu)存在顯著差異。理解這些差異有助于根據(jù)實(shí)際業(yè)務(wù)需求做出合理選型。

5.1.1 API設(shè)計(jì)風(fēng)格差異:鏈?zhǔn)秸{(diào)用 vs 回調(diào)驅(qū)動

JSZip 最突出的優(yōu)勢在于其 面向?qū)ο?+ 鏈?zhǔn)秸{(diào)用 的編程模型。整個壓縮過程被抽象為一個 JSZip 實(shí)例,所有添加文件、設(shè)置參數(shù)、生成輸出等動作都可以通過連續(xù)的方法調(diào)用來完成,極大提升了代碼可讀性與維護(hù)性。

const zip = new JSZip();
zip.file("hello.txt", "Hello World")
   .folder("images")
   .file("images/cat.jpg", imageData, { base64: true });

上述代碼展示了典型的鏈?zhǔn)綄懛ǎ合葎?chuàng)建實(shí)例,然后通過 .file() 添加文本內(nèi)容,再通過 .folder() 創(chuàng)建子目錄并繼續(xù)添加圖片資源。這種結(jié)構(gòu)非常接近自然語言描述,便于團(tuán)隊(duì)協(xié)作和后期擴(kuò)展。

相比之下,Zip.js 使用的是基于事件回調(diào)或 Promise 的異步流式 API:

const writer = new zip.BlobWriter();
const reader = new zip.BlobReader(blob);
const zipWriter = new zip.ZipWriter(writer);
zipWriter.add("hello.txt", new zip.TextReader("Hello World"))
         .then(() => zipWriter.close());

雖然也具備一定的模塊化特征,但整體邏輯分散于多個嵌套的 .then() 調(diào)用中,尤其在處理復(fù)雜目錄結(jié)構(gòu)時容易導(dǎo)致“回調(diào)地獄”,降低代碼整潔度。

特性JSZipZip.js
編程范式面向?qū)ο?+ 鏈?zhǔn)秸{(diào)用流式 + 回調(diào)/Promise
學(xué)習(xí)曲線較低,API 直觀中等,需理解 Reader/Writer 模型
異步控制基于 Promise支持 Promise 和事件驅(qū)動
內(nèi)存管理粒度自動管理可精細(xì)控制緩沖區(qū)大小
文件增量寫入支持是(支持流式寫入)

該表格清晰地反映了兩種庫的設(shè)計(jì)哲學(xué): JSZip 更偏向于開發(fā)效率與簡潔性 ,而 Zip.js 更注重底層可控性與資源優(yōu)化 。

mermaid 流程圖:API調(diào)用路徑對比
graph TD
    A[用戶觸發(fā)壓縮] --> B{選擇庫}
    B --> C[JSZip]
    B --> D[Zip.js]
    C --> C1[創(chuàng)建 JSZip 實(shí)例]
    C1 --> C2[調(diào)用 file() / folder()]
    C2 --> C3[鏈?zhǔn)綐?gòu)建結(jié)構(gòu)]
    C3 --> C4[generateAsync() 輸出 Blob]
    D --> D1[創(chuàng)建 BlobWriter]
    D1 --> D2[初始化 ZipWriter]
    D2 --> D3[add() 添加條目]
    D3 --> D4[close() 完成歸檔]

此流程圖揭示了兩者在執(zhí)行路徑上的根本區(qū)別——JSZip 將所有操作封裝在一個統(tǒng)一上下文中,而 Zip.js 則依賴多個獨(dú)立組件協(xié)同工作,增加了認(rèn)知負(fù)擔(dān)。

5.1.2 瀏覽器兼容性與體積大小比較

在現(xiàn)代前端開發(fā)中,庫的體積直接影響首屏加載速度,尤其是在移動端或弱網(wǎng)環(huán)境下。JSZip 的生產(chǎn)版本經(jīng)過壓縮后約為 14KB(gzip) ,未壓縮約 60KB,屬于中小型庫范疇;而 Zip.js(包含所有 Reader/Writer 模塊)總大小可達(dá) 80KB 以上(未壓縮) ,明顯更大。

但從兼容性角度看,JSZip 對舊版瀏覽器的支持更為完善。它通過內(nèi)置的 polyfill 機(jī)制保障在 IE10+ 及主流現(xiàn)代瀏覽器中的穩(wěn)定運(yùn)行,特別適用于企業(yè)級管理系統(tǒng)這類仍需支持較老環(huán)境的項(xiàng)目。Zip.js 雖然也支持大多數(shù)現(xiàn)代瀏覽器,但在 Safari 早期版本或某些 WebView 環(huán)境下可能出現(xiàn) Blob 分片異常問題。

此外,JSZip 的生態(tài)更為成熟,擁有官方文檔、活躍社區(qū)以及豐富的插件體系(如 jszip-utils、FileSaver.js 集成示例)。開發(fā)者可通過 npm 快速安裝并配合 webpack 等工具進(jìn)行按需打包:

npm install jszip
import JSZip from 'jszip';
// 動態(tài)導(dǎo)入以減少初始包體積
async function loadJSZip() {
  const module = await import('jszip');
  return new module.default();
}

這種方式允許將 JSZip 的加載延遲至真正需要時,進(jìn)一步優(yōu)化應(yīng)用啟動性能。

綜上所述,在大多數(shù)常規(guī)應(yīng)用場景下(如導(dǎo)出報表、打包附件、離線資源歸檔), JSZip 憑借其簡潔 API 和良好兼容性成為首選方案 ;而在涉及超大文件流式處理、自定義壓縮算法或嚴(yán)格內(nèi)存控制的特殊場景中,Zip.js 更具優(yōu)勢。

5.2 使用JSZip實(shí)現(xiàn)壓縮操作

在掌握 JSZip 的定位之后,接下來進(jìn)入實(shí)戰(zhàn)階段——如何利用該庫實(shí)現(xiàn)完整的前端壓縮功能。本節(jié)重點(diǎn)介紹其實(shí)例化方式、多類型數(shù)據(jù)輸入支持以及異步生成機(jī)制,幫助開發(fā)者構(gòu)建健壯的 ZIP 構(gòu)建流程。

5.2.1 創(chuàng)建JSZip實(shí)例并添加文件

每個壓縮任務(wù)都始于一個 JSZip 類的實(shí)例。該實(shí)例充當(dāng) ZIP 包的容器,負(fù)責(zé)組織內(nèi)部文件結(jié)構(gòu)、維護(hù)元數(shù)據(jù),并最終生成二進(jìn)制輸出。

const zip = new JSZip();
// 添加純文本文件
zip.file("readme.md", "# 歡迎使用JSZip");
// 添加空目錄
const imgFolder = zip.folder("assets/images");
// 向子目錄添加文件
imgFolder.file("logo.png", pngData, { binary: true });
  • new JSZip() :構(gòu)造函數(shù)無參數(shù),返回一個干凈的 ZIP 上下文。
  • .file(name, data) :接受文件名和任意數(shù)據(jù)源,自動識別字符串或二進(jìn)制類型。
  • .folder(path) :創(chuàng)建虛擬目錄,支持嵌套路徑(如 "src/js" )。

值得注意的是,JSZip 并不立即寫入磁盤,而是將所有內(nèi)容保留在內(nèi)存中,直到調(diào)用 generateAsync() 才進(jìn)行序列化。這意味著可以在任意時刻動態(tài)修改 ZIP 內(nèi)容,例如條件性地刪除某個文件:

if (shouldExcludeLog) {
  zip.remove("debug.log");
}

這種靈活性使得 JSZip 非常適合用于構(gòu)建帶有用戶交互選項(xiàng)的打包系統(tǒng)(如“是否包含日志文件”復(fù)選框)。

5.2.2 支持Base64、Uint8Array等多種輸入格式

JSZip 能夠解析多種原始數(shù)據(jù)格式,包括但不限于:

輸入類型示例值是否需指定選項(xiàng)
字符串"Hello"
Base64 字符串"SGVsbG8=" base64: true
Uint8Arraynew Uint8Array([72, 101, 108, 108, 111]) binary: true
ArrayBufferresponse.arrayBuffer() binary: true
Blobnew Blob(["data"], {type: "text/plain"}) 自動識別
// 示例:從 Base64 圖片創(chuàng)建文件
const base64Image = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUg...";
const cleaned = base64Image.split(",")[1]; // 移除 data URL 前綴
zip.file("photo.png", cleaned, { base64: true });
// 示例:從 fetch 獲取的 ArrayBuffer 添加 PDF
fetch('/report.pdf')
  .then(res => res.arrayBuffer())
  .then(buffer => {
    zip.file("report.pdf", buffer, { binary: true });
  });

參數(shù)說明
- { base64: true } :告知 JSZip 數(shù)據(jù)是 Base64 編碼,需先解碼為二進(jìn)制;
- { binary: true } :表示傳入的是原始字節(jié)流,避免字符編碼轉(zhuǎn)換錯誤;
- 若省略這些標(biāo)志,可能導(dǎo)致中文亂碼或圖像損壞。

這一點(diǎn)在處理非 UTF-8 編碼內(nèi)容(如二進(jìn)制圖片、加密數(shù)據(jù))時尤為重要。正確設(shè)置選項(xiàng)可確保文件在解壓后保持原始完整性。

5.2.3 generateAsync()方法生成異步ZIP輸出

由于 ZIP 生成涉及大量字節(jié)拼接與壓縮計(jì)算,JSZip 提供了 generateAsync() 方法以異步方式完成最終打包,防止阻塞主線程。

zip.generateAsync({
  type: "blob",
  compression: "DEFLATE",
  compressionOptions: { level: 9 }
}).then(blob => {
  const url = URL.createObjectURL(blob);
  const a = document.createElement("a");
  a.href = url;
  a.download = "archive.zip";
  a.click();
});
  • type : 輸出格式,常用 "blob" (用于下載)、 "base64" "uint8array" ;
  • compression : 壓縮算法,默認(rèn)為 "STORE" (無壓縮)或 "DEFLATE" (標(biāo)準(zhǔn)壓縮);
  • compressionOptions.level : 壓縮級別(1~9),數(shù)值越高壓縮率越大但耗時越長。

該方法返回一個 Promise,因此可以輕松集成進(jìn) async/await 結(jié)構(gòu):

try {
  const blob = await zip.generateAsync({ type: "blob" });
  saveAs(blob, "project.zip"); // 使用 FileSaver.js
} catch (e) {
  console.error("生成失敗:", e);
}

結(jié)合進(jìn)度監(jiān)聽功能(需自行實(shí)現(xiàn)分塊追蹤),還可為用戶提供實(shí)時反饋體驗(yàn)。

5.3 使用JSZip實(shí)現(xiàn)解壓縮操作

除了壓縮,JSZip 同樣提供了強(qiáng)大的解壓能力,能夠?qū)⑸蟼鞯?ZIP 文件完整還原為文件樹結(jié)構(gòu),并提取其中的內(nèi)容供前端展示或進(jìn)一步處理。

5.3.1 loadAsync()加載ArrayBuffer格式的ZIP文件

解壓的第一步是將用戶選擇的 ZIP 文件讀取為 ArrayBuffer ,然后交由 JSZip 解析。

const input = document.querySelector("#zip-file");
input.addEventListener("change", async (e) => {
  const file = e.target.files[0];
  const arrayBuffer = await file.arrayBuffer();
  const zip = await JSZip.loadAsync(arrayBuffer);
  console.log(zip.files); // 輸出文件列表
});
  • file.arrayBuffer() :HTML5 FileReader 的現(xiàn)代替代方案,直接獲取二進(jìn)制數(shù)據(jù);
  • JSZip.loadAsync(buffer) :異步加載 ZIP 結(jié)構(gòu),返回帶 .files 屬性的對象;
  • .files 是一個鍵值對集合,鍵為文件路徑(含目錄),值為 ZipObject 實(shí)例。

該方法自動處理 ZIP 中央目錄解析、本地頭校驗(yàn)等工作,屏蔽了底層細(xì)節(jié)。

5.3.2 遍歷files對象提取各成員內(nèi)容

一旦 ZIP 被成功加載,即可遍歷 .files 來訪問每一個條目:

for (const [filename, zipEntry] of Object.entries(zip.files)) {
  if (zipEntry.dir) continue; // 跳過目錄
  const content = await zipEntry.async("text"); // 可選: "text", "binarystring", "arraybuffer"
  console.log(`${filename}: ${content.substring(0, 100)}...`);
}
  • zipEntry.async(type) :異步提取內(nèi)容,支持多種輸出格式;
  • 支持過濾隱藏文件(如 macOS 的 .DS_Store )或系統(tǒng)元數(shù)據(jù)( __MACOSX/ 目錄):
const isValidFile = (name) =>
  !name.startsWith("__MACOSX/") &&
  !path.basename(name).startsWith(".");
const validFiles = Object.keys(zip.files).filter(isValidFile);

這在構(gòu)建安全的文檔查看器時極為重要,避免無意中暴露敏感信息。

5.3.3 支持過濾隱藏文件和系統(tǒng)元數(shù)據(jù)

許多操作系統(tǒng)會在 ZIP 中插入額外元數(shù)據(jù)文件,若不加過濾可能干擾業(yè)務(wù)邏輯。以下是一個通用清理函數(shù):

function filterUselessEntries(files) {
  return Object.fromEntries(
    Object.entries(files).filter(([name, entry]) => {
      return !(
        name.includes("__MACOSX") ||
        name.endsWith(".DS_Store") ||
        entry.dir && name.startsWith(".")
      );
    })
  );
}
const cleanFiles = filterUselessEntries(zip.files);

此策略可有效提升用戶體驗(yàn),防止無效文件污染結(jié)果列表。

5.4 高級特性:密碼保護(hù)與壓縮級別設(shè)置

盡管瀏覽器端加密 ZIP 存在局限,JSZip 仍提供部分高級功能用于增強(qiáng)安全性與性能控制。

5.4.1 加密ZIP文件的實(shí)現(xiàn)方式(有限支持)

JSZip 不原生支持 AES 加密 ,僅能生成傳統(tǒng)的 ZIP 2.0 加密(弱加密,易破解),且現(xiàn)代瀏覽器大多不推薦使用。因此,若需強(qiáng)加密,建議采用以下替代方案:

// 方案:先壓縮 → 再使用 CryptoJS 加密整個 Blob
import CryptoJS from 'crypto-js';
const blob = await zip.generateAsync({ type: "arraybuffer" });
const wordArray = CryptoJS.lib.WordArray.create(new Uint8Array(blob));
const encrypted = CryptoJS.AES.encrypt(wordArray, "secret-key").toString();
// 下載加密后的數(shù)據(jù)
const encryptedBlob = new Blob([encrypted], { type: "text/plain" });

注意:此方法加密的是整個 ZIP 流,解密需先還原再交給 JSZip 處理。

5.4.2 調(diào)整compression參數(shù)控制打包效率

通過調(diào)整 compression level 參數(shù),可在壓縮比與性能間取得平衡:

zip.generateAsync({
  type: "blob",
  compression: "DEFLATE",
  compressionOptions: {
    level: 6 // 推薦值:6(兼顧速度與壓縮率)
  }
})

測試表明,在 10MB 文本數(shù)據(jù)上:
- level: 1 :耗時 ~800ms,輸出 ~3.5MB
- level: 6 :耗時 ~1.4s,輸出 ~2.8MB
- level: 9 :耗時 ~2.3s,輸出 ~2.6MB

因此,對于大型文件批量打包,建議設(shè)為 level: 6 ,避免卡頓影響交互流暢性。

6. 文件夾批量文件讀取與處理策略

6.1 WebKit目錄遍歷API的使用條件

現(xiàn)代瀏覽器中,允許用戶選擇整個文件夾進(jìn)行操作的功能依賴于 WebKit 瀏覽器引擎 提供的專有擴(kuò)展 API —— directory webkitdirectory 屬性。盡管該功能尚未被納入正式 HTML 標(biāo)準(zhǔn),但在 Chrome、Edge 及部分基于 Chromium 的瀏覽器中已得到良好支持。

6.1.1 requestDirectoryRead()方法的啟用前提

要實(shí)現(xiàn)對文件夾內(nèi)容的遞歸讀取,必須通過帶有 webkitdirectory 屬性的 <input> 元素觸發(fā)用戶主動選擇行為:

<input type="file" id="folderInput" webkitdirectory directory multiple />

?? 注意: directory 是較新的標(biāo)準(zhǔn)屬性,而 webkitdirectory 是其在早期 Chromium 中的實(shí)現(xiàn)名稱,目前仍需保留以確保兼容性。

當(dāng)用戶選擇一個目錄后,返回的 DataTransferItemList 將包含所有嵌套條目(包括子目錄和文件),每個條目可通過 .webkitGetAsEntry() 方法獲取 FileSystemEntry 對象。

document.getElementById('folderInput').addEventListener('change', async (e) => {
    const items = e.target.files;
    for (let file of items) {
        const entry = file.webkitRelativePath ? 
            file /* 可直接訪問 */ : 
            e.target.webkitEntries?.find(e => e.name === file.name);
        if (entry) {
            console.log('Entry:', entry.name, entry.isDirectory ? 'is dir' : 'is file');
        }
    }
});

此方法僅在 HTTPS 環(huán)境或本地開發(fā)服務(wù)器(localhost)下生效,且必須由用戶顯式交互觸發(fā),防止自動掃描用戶磁盤。

6.1.2 判斷瀏覽器是否支持文件夾選擇(directory entry)

為提升健壯性,應(yīng)在運(yùn)行時檢測當(dāng)前環(huán)境是否支持目錄選擇功能:

function isDirectoryUploadSupported() {
    const input = document.createElement('input');
    return 'webkitdirectory' in input || 'directory' in input;
}
if (!isDirectoryUploadSupported()) {
    alert('當(dāng)前瀏覽器不支持文件夾上傳,請使用 Chrome/Edge 最新版');
}

此外,可通過特性探測進(jìn)一步驗(yàn)證 webkitGetAsEntry 是否可用:

function supportsFileSystemEntries(dataTransfer) {
    return Array.from(dataTransfer.items)
        .some(item => item.kind === 'file' && item.webkitGetAsEntry);
}

6.2 遞歸讀取嵌套文件結(jié)構(gòu)

為了完整構(gòu)建用戶所選文件夾的樹形結(jié)構(gòu),需要結(jié)合 FileSystemDirectoryReader 接口實(shí)現(xiàn)異步遞歸遍歷。

6.2.1 使用readEntries()循環(huán)讀取目錄條目

readEntries() 方法以批處理方式返回目錄中的條目列表,需循環(huán)調(diào)用直至返回空數(shù)組:

async function readAllEntries(directoryEntry) {
    const reader = directoryEntry.createReader();
    let entries = [];
    let read;
    do {
        read = await new Promise(resolve => reader.readEntries(resolve));
        entries = entries.concat(Array.from(read));
    } while (read.length > 0);
    return entries;
}

6.2.2 區(qū)分文件與子目錄并建立樹形結(jié)構(gòu)映射

以下函數(shù)將整個目錄結(jié)構(gòu)轉(zhuǎn)換為 JSON 樹狀模型,便于后續(xù)壓縮或展示:

async function buildFileTree(entry, pathPrefix = '') {
    const node = {
        name: entry.name,
        fullPath: pathPrefix + entry.name,
        children: [],
        isDirectory: false,
        size: null
    };
    if (entry.isFile) {
        node.isDirectory = false;
        return new Promise((resolve) =>
            entry.file(file => {
                node.size = file.size;
                resolve(node);
            })
        );
    }
    if (entry.isDirectory) {
        node.isDirectory = true;
        const children = await readAllEntries(entry);
        node.children = await Promise.all(
            children.map(child =>
                buildFileTree(child, node.fullPath + '/')
            )
        );
        return node;
    }
    return node;
}

示例輸出結(jié)構(gòu)如下:

節(jié)點(diǎn)路徑類型大?。ㄗ止?jié))
/project目錄-
/project/index.html文件2048
/project/js/app.js文件5120
/project/css/theme.css文件3072
/project/assets目錄-
/project/assets/logo.png文件81920
/project/docs目錄-
/project/docs/readme.txt文件1024
/project/node_modules目錄-
/project/node_modules/lodash.js文件72000
/project/config.json文件512

該樹形結(jié)構(gòu)可用于:
- 構(gòu)造 ZIP 歸檔時保持原始路徑層級
- 在前端 UI 中渲染可展開的文件瀏覽器
- 實(shí)現(xiàn)搜索、過濾與權(quán)限控制邏輯

6.3 批量處理中的性能優(yōu)化方案

大規(guī)模文件夾處理容易造成主線程阻塞,影響用戶體驗(yàn),因此必須引入異步與并發(fā)機(jī)制。

6.3.1 分片讀取避免主線程阻塞

對于含有上千個文件的目錄,應(yīng)采用“微任務(wù)隊(duì)列”方式進(jìn)行分片處理:

async function processFilesInBatches(files, processor, batchSize = 10) {
    for (let i = 0; i < files.length; i += batchSize) {
        const batch = files.slice(i, i + batchSize);
        await Promise.all(batch.map(processor));
        // 插入微任務(wù)等待,釋放主線程
        await new Promise(resolve => setTimeout(resolve, 0));
    }
}

這樣可在每批次處理后讓出執(zhí)行權(quán),防止頁面凍結(jié)。

6.3.2 使用Worker線程進(jìn)行后臺壓縮計(jì)算

將耗時的壓縮任務(wù)移至 Web Worker ,利用多核 CPU 并行處理:

// main.js
const worker = new Worker('/workers/zip-worker.js');
worker.postMessage({ action: 'startZip', treeData: fileTree });
worker.onmessage = function(e) {
    if (e.data.blob) {
        const url = URL.createObjectURL(e.data.blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = 'archive.zip';
        a.click();
    }
};
// workers/zip-worker.js
importScripts('https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js');
self.onmessage = async function(e) {
    const zip = new JSZip();
    const addFilesRecursively = (node, parentFolder) => {
        if (node.isDirectory) {
            const folder = parentFolder.folder(node.name);
            node.children.forEach(child => addFilesRecursively(child, folder));
        } else {
            parentFolder.file(node.name, fetch(node.fullPath).then(r => r.arrayBuffer()));
        }
    };
    addFilesRecursively(e.data.treeData, zip);
    const blob = await zip.generateAsync({ type: 'blob' });
    self.postMessage({ blob }, [blob]);
};

? 使用 transferable objects (如 [blob] )可高效傳遞大數(shù)據(jù),減少序列化開銷。

6.4 安全性考慮與用戶數(shù)據(jù)處理最佳實(shí)踐

6.4.1 防止路徑遍歷攻擊(Path Traversal)的風(fēng)險

即使在瀏覽器沙箱內(nèi),也應(yīng)校驗(yàn)文件路徑合法性,防止惡意構(gòu)造的相對路徑逃逸預(yù)期范圍:

function isValidPath(path) {
    const normalized = path.replace(/\\/g, '/').replace(/\.\.\//g, '');
    return !normalized.includes('../') && !normalized.startsWith('/');
}

同時,在生成 ZIP 包時應(yīng)剝離絕對前綴 / C:\ ,統(tǒng)一使用相對路徑。

6.4.2 明確告知用戶數(shù)據(jù)用途并提供清除機(jī)制

在處理敏感文檔時,應(yīng)彈出提示說明數(shù)據(jù)不會上傳至服務(wù)器,并提供一鍵清理內(nèi)存緩存功能:

function clearMemoryCache() {
    globalFileReferences.clear();
    URL.revokeObjectURLs(); // 清理 createObjectURL 創(chuàng)建的所有鏈接
    caches.delete('local-file-cache'); // 若使用 Cache API
}

6.4.3 敏感文件類型檢測與自動攔截策略

可通過擴(kuò)展名黑名單阻止?jié)撛陲L(fēng)險文件被加載:

const BLOCKED_EXTS = ['.exe', '.bat', '.sh', '.dll', '.ps1', '.cmd'];
function isBlockedFile(file) {
    return BLOCKED_EXTS.some(ext => file.name.toLowerCase().endsWith(ext));
}
// 使用示例
Array.from(input.files).forEach(file => {
    if (isBlockedFile(file)) {
        console.warn(`已阻止加載危險文件: ${file.name}`);
        return;
    }
    // 正常處理流程...
});

此外,建議結(jié)合 MIME 類型探測增強(qiáng)安全性:

async function getMimeType(file) {
    const arr = await file.slice(0, 4).arrayBuffer();
    const view = new Uint8Array(arr);
    const hex = Array.prototype.map.call(view, b => b.toString(16).padStart(2, '0')).join('');
    switch (hex) {
        case '504b0304': return 'application/zip';
        case '89504e47': return 'image/png';
        default: return file.type || 'unknown';
    }
}

到此這篇關(guān)于JavaScript實(shí)現(xiàn)文件夾壓縮與解壓縮完整指南的文章就介紹到這了,更多相關(guān)js文件夾壓縮與解壓縮內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 將input框中輸入內(nèi)容顯示在相應(yīng)的div中【三種方法可選】

    將input框中輸入內(nèi)容顯示在相應(yīng)的div中【三種方法可選】

    本篇文章主要介紹了在input框中輸入內(nèi)容,會相應(yīng)的顯示在下面的div中的不同做法:js方法;jQuery方法;AngularJs方法,具有很好的參考價值。下面跟著小編一起來看下吧
    2017-05-05
  • js在Firefox與IE中對DOM對像的引用的比較

    js在Firefox與IE中對DOM對像的引用的比較

    直接用ID屬性進(jìn)行引用 直接用NAME屬性進(jìn)行引用 使用getElementById(),getElementsByName(),getElementsByTagName()進(jìn)行引用
    2009-06-06
  • 原生js實(shí)現(xiàn)模擬滾動條

    原生js實(shí)現(xiàn)模擬滾動條

    本文給大家分享的是使用原生javascript實(shí)現(xiàn)模擬滾動條的方法及代碼,效果非常棒,有需要的小伙伴可以參考下。
    2015-06-06
  • javascript前端和后臺進(jìn)行數(shù)據(jù)交互方法示例

    javascript前端和后臺進(jìn)行數(shù)據(jù)交互方法示例

    這篇文章主要介紹了javascript前端和后臺進(jìn)行數(shù)據(jù)交互方法示例,文章通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • JavaScript事件處理程序詳解

    JavaScript事件處理程序詳解

    這篇文章主要為大家詳細(xì)介紹了JavaScript事件處理程序的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-09-09
  • Typescript中Type check類型檢查的實(shí)現(xiàn)

    Typescript中Type check類型檢查的實(shí)現(xiàn)

    本文主要介紹了Typescript中Type check類型檢查的實(shí)現(xiàn),類型檢查是一項(xiàng)非常重要的特性,它可以幫助你捕獲潛在的錯誤,下面就來詳細(xì)的介紹一下如何實(shí)現(xiàn),感興趣的可以了解一下
    2025-10-10
  • 原生JS實(shí)現(xiàn)音樂播放器的示例代碼

    原生JS實(shí)現(xiàn)音樂播放器的示例代碼

    這篇文章主要介紹了原生JS實(shí)現(xiàn)音樂播放器的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • 解決layui laydate 時間控件一閃而過的問題

    解決layui laydate 時間控件一閃而過的問題

    今天小編就為大家分享一篇解決layui laydate 時間控件一閃而過的問題,具有好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2019-09-09
  • 小程序?qū)崿F(xiàn)單選多選功能

    小程序?qū)崿F(xiàn)單選多選功能

    這篇文章主要為大家詳細(xì)介紹了小程序?qū)崿F(xiàn)單選多選功能,可自定義樣式,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-11-11
  • 深入理解JavaScript程序中內(nèi)存泄漏

    深入理解JavaScript程序中內(nèi)存泄漏

    這篇文章主要幫助大家深入理解JavaScript程序中內(nèi)存泄漏問題,感興趣的小伙伴們可以參考一下
    2016-03-03

最新評論

安康市| 文山县| 花垣县| 舞钢市| 屯昌县| 南安市| 荃湾区| 罗平县| 馆陶县| 垫江县| 景泰县| 海阳市| 临武县| 彩票| 宁海县| 安陆市| 萝北县| 五大连池市| 中山市| 赞皇县| 吉安县| 青浦区| 绥化市| 大同县| 旅游| 顺昌县| 佳木斯市| 平舆县| 抚州市| 洮南市| 青田县| 集安市| 酉阳| 涟源市| 上栗县| 阳原县| 呼玛县| 石河子市| 平定县| 宜州市| 涟源市|