JavaScript實(shí)現(xiàn)文件夾壓縮與解壓縮完整攻略
簡介:在瀏覽器環(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 | string | MIME 類型,如 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 | number | 65536 | 每次讀取的字節(jié)塊大小 |
level | number | 6 | 壓縮級別(0~9,0為無壓縮) |
useWebWorkers | boolean | true | 是否啟用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)地獄”,降低代碼整潔度。
| 特性 | JSZip | Zip.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 |
| Uint8Array | new Uint8Array([72, 101, 108, 108, 111]) | binary: true |
| ArrayBuffer | response.arrayBuffer() | binary: true |
| Blob | new 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中的不同做法:js方法;jQuery方法;AngularJs方法,具有很好的參考價值。下面跟著小編一起來看下吧2017-05-05
javascript前端和后臺進(jìn)行數(shù)據(jù)交互方法示例
這篇文章主要介紹了javascript前端和后臺進(jìn)行數(shù)據(jù)交互方法示例,文章通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08
Typescript中Type check類型檢查的實(shí)現(xiàn)
本文主要介紹了Typescript中Type check類型檢查的實(shí)現(xiàn),類型檢查是一項(xiàng)非常重要的特性,它可以幫助你捕獲潛在的錯誤,下面就來詳細(xì)的介紹一下如何實(shí)現(xiàn),感興趣的可以了解一下2025-10-10

