1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > 【Web技术】959- JavaScript 如何解压 ZIP 文件?

【Web技术】959- JavaScript 如何解压 ZIP 文件?

时间:2024-05-16 15:21:55

相关推荐

【Web技术】959- JavaScript 如何解压 ZIP 文件?

相信大家对 ZIP 文件都不会陌生,当你要打开本地的 ZIP 文件时,你就需要先安装支持解压 ZIP 文件的解压软件。但如果预解压的 ZIP 文件在服务器上,我们应该如何处理呢?

最简单的一种方案就是把文件下载到本地,然后使用支持 ZIP 格式的解压软件进行解压。那么能不能在线解压 ZIP 文件呢?答案是可以的,接下来阿宝哥将介绍浏览器解压和服务器解压两种在线解压 ZIP 文件的方案。

在介绍在线解压 ZIP 文件的两种方案前,我们先来简单了解一下 ZIP 文件格式。

一、ZIP 格式简介

ZIP 文件格式是一种数据压缩和文档储存的文件格式,原名 Deflate,发明者为菲尔·卡茨(Phil Katz),他于 1989 年 1 月公布了该格式的资料。ZIP 通常使用后缀名 “.zip”,它的 MIME 格式为 “application/zip”。目前,ZIP 格式属于几种主流的压缩格式之一,其竞争者包括RAR 格式以及开放源码的 7z 格式。

ZIP 是一种相当简单的分别压缩每个文件的存档格式,分别压缩文件允许不必读取另外的数据而检索独立的文件。理论上,这种格式允许对不同的文件使用不同的算法。然而,在实际上,ZIP 大多数都是在使用卡茨(Katz)的 DEFLATE 算法。

简单介绍完 ZIP 格式,接下来阿宝哥先来介绍基于JSZip这个库的浏览器解压方案。

二、浏览器解压方案

JSZip 是一个用于创建、读取和编辑.zip文件的 JavaScript 库,该库支持大多数浏览器,具体的兼容性如下图所示:

其实有了 JSZip 这个库的帮助,要实现浏览器端在线解压 ZIP 文件的功能并不难。因为官方已经为我们提供了解压本地文件、解压远程文件和生成 ZIP 文件的完整示例。好的,废话不多说,下面我们来一步步实现在线解压 ZIP 文件的功能。

2.1 定义工具类

浏览器端在线解压 ZIP 文件的功能,可以拆分为下载 ZIP 文件、解析 ZIP 文件和展示 ZIP 文件3 个小功能。考虑到功能复用性,阿宝哥把下载 ZIP 文件和解析 ZIP 文件的逻辑封装在ExeJSZip类中:

classExeJSZip{//用于获取url地址对应的文件内容getBinaryContent(url,progressFn=()=>{}){returnnewPromise((resolve,reject)=>{if(typeofurl!=="string"||!/https?:/.test(url))reject(newError("url参数不合法"));JSZipUtils.getBinaryContent(url,{//JSZipUtils来自于jszip-utils这个库progress:progressFn,callback:(err,data)=>{if(err){reject(err);}else{resolve(data);}},});});}//遍历Zip文件asynciterateZipFile(data,iterationFn){if(typeofiterationFn!=="function"){thrownewError("iterationFn不是函数类型");}letzip;try{zip=awaitJSZip.loadAsync(data);//JSZip来自于jszip这个库zip.forEach(iterationFn);returnzip;}catch(error){thrownewerror();}}}

2.2 在线解压 ZIP 文件

利用ExeJSZip类的实例,我们就可以很容易实现在线解压 ZIP 文件的功能:

html 代码

<p><label>请输入ZIP文件的线上地址:</label><inputtype="text"id="zipUrl"/></p><buttonid="unzipBtn"onclick="unzipOnline()">在线解压</button><pid="status"></p><ulid="fileList"></ul>

JS 代码

constzipUrlEle=document.querySelector("#zipUrl");conststatusEle=document.querySelector("#status");constfileList=document.querySelector("#fileList");constexeJSZip=newExeJSZip();//执行在线解压操作asyncfunctionunzipOnline(){fileList.innerHTML="";statusEle.innerText="开始下载文件...";constdata=awaitexeJSZip.getBinaryContent(zipUrlEle.value,handleProgress);letitems="";awaitexeJSZip.iterateZipFile(data,(relativePath,zipEntry)=>{items+=`<liclass=${zipEntry.dir?"caret":"indent"}>${zipEntry.name}</li>`;});statusEle.innerText="ZIP文件解压成功";fileList.innerHTML=items;}//处理下载进度functionhandleProgress(progressData){const{percent,loaded,total}=progressData;if(loaded===total){statusEle.innerText="文件已下载,努力解压中";}}

好了,在浏览器端如何通过 JSZip 这个库来实现在线解压 ZIP 文件的功能已经介绍完了,我们来看一下以上示例的运行结果:

现在我们已经可以在线解压 ZIP 文件了,这时有的小伙伴可能会问,能否预览解压后的文件呢?答案是可以的,因为 JSZip 这个库为我们提供了fileAPI,通过这个 API 我们就可以读取指定文件中的内容。比如这样使用zip.file("amount.txt").async("arraybuffer"),之后我们就可以执行对应的操作来实现文件预览的功能。

需要注意的是,基于 JSZip 的方案并不是完美的,它存在一些限制。比如它不支持解压加密的 ZIP 文件,当解压较大的文件时,在 IE 10 以下的浏览器可能会出现闪退问题。此外,它还有一些其它的限制,这里阿宝哥就不详细说明了。感兴趣的小伙伴,可以阅读Limitations of JSZip文章中的相关内容。

既然浏览器解压方案存在一些弊端,特别是在线解压大文件的情形,要解决该问题,我们可以考虑使用服务器解压方案。

三、服务器解压方案

服务器解压方案就是允许用户通过文件 ID 或文件名进行在线解压,接下来阿宝哥将基于 koa 和 node-stream-zip 这两个库来介绍如何实现服务器在线解压 ZIP 文件的功能。如果你对 koa 还不了解的话,建议你先大致阅读一下 koa 的官方文档。

constpath=require("path");constKoa=require("koa");constcors=require("@koa/cors");constRouter=require("@koa/router");constStreamZip=require("node-stream-zip");constapp=newKoa();constrouter=newRouter();constZIP_HOME=path.join(__dirname,"zip");//ZIP文件的根目录constUnzipCaches=newMap();//保存已解压的文件信息router.get("/",async(ctx)=>{ctx.body="服务端在线解压ZIP文件示例(阿宝哥)";});//注册中间件app.use(cors());app.use(router.routes()).use(router.allowedMethods());app.listen(3000,()=>{console.log("appstartingatport3000");});

在以上代码中,我们使用了@koa/cors@koa/router两个中间件并创建了一个简单的 Koa 应用程序。基于上述的代码,我们来注册一个用于处理在线解压指定文件名的路由。

3.1 根据文件名解压指定 ZIP 文件

app.js

router.get("/unzip/:name",async(ctx)=>{constfileName=ctx.params.name;letfilteredEntries;try{if(UnzipCaches.has(fileName)){//优先从缓存中获取filteredEntries=UnzipCaches.get(fileName);}else{constzip=newStreamZip.async({file:path.join(ZIP_HOME,fileName)});constentries=awaitzip.entries();filteredEntries=Object.values(entries).map((entry)=>{return{name:entry.name,size:entry.size,dir:entry.isDirectory,};});awaitzip.close();UnzipCaches.set(fileName,filteredEntries);}ctx.body={status:"success",entries:filteredEntries,};}catch(error){ctx.body={status:"error",msg:`在线解压${fileName}文件失败`,};}});

在以上代码中,我们通过ZIP_HOMEfileName获得文件的最终路径,然后使用StreamZip对象来执行解压操作。为了避免重复执行解压操作,阿宝哥定义了一个UnzipCaches缓存对象,用来保存已解压的文件信息。定义好上述路由,下面我们来验证一下对应的功能。

3.2 在线解压 ZIP 文件

html 代码

<p><label>请输入ZIP文件名:</label><inputtype="text"id="fileName"value="kl_161828427993677"/></p><buttonid="unzipBtn"onclick="unzipOnline()">在线解压</button><pid="status"></p><ulid="fileList"></ul>

JS 代码

constfileList=document.querySelector("#fileList");constfileNameEle=document.querySelector("#fileName");constrequest=axios.create({baseURL:"http://localhost:3000/",timeout:10000,});asyncfunctionunzipOnline(){constfileName=fileNameEle.value;if(!fileName)return;constresponse=awaitrequest.get(`unzip/${fileName}`);if(response.data&&response.data.status==="success"){constentries=response.data.entries;letitems="";entries.forEach((zipEntry)=>{items+=`<liclass=${zipEntry.dir?"caret":"indent"}>${zipEntry.name}</li>`;});fileList.innerHTML=items;}}

以上示例成功运行后的结果如下图所示:

现在我们已经实现根据文件名解压指定 ZIP 文件,那么我们可以预览压缩文件中指定路径的文件么?答案也是可以的,利用zip对象提供的entryData(entry: string | ZipEntry): Promise<Buffer>方法就可以读取指定路径下文件的内容。

3.3 预览 ZIP 文件中指定路径的文件

app.js

router.get("/unzip/:name/entry",async(ctx)=>{constfileName=ctx.params.name;//ZIP压缩文件名constentryPath=ctx.query.path;//文件的路径try{constzip=newStreamZip.async({file:path.join(ZIP_HOME,fileName)});constentryData=awaitzip.entryData(entryPath);awaitzip.close();ctx.body={status:"success",entryData:entryData,};}catch(error){ctx.body={status:"error",msg:`读取${fileName}中${entryPath}文件失败`,};}});

在以上代码中,我们通过zip.entryData方法来读取指定路径的文件内容,它返回的是一个Buffer对象。当前端接收到该数据时,还需要把接收到的Buffer对象转换为ArrayBuffer对象,对应的处理方式如下所示:

functiontoArrayBuffer(buf){letab=newArrayBuffer(buf.length);letview=newUint8Array(ab);for(leti=0;i<buf.length;++i){view[i]=buf[i];}returnab;}

定义完toArrayBuffer函数之后,我们就可以通过调用app.js定义的 API 来实现预览功能,具体的代码如下所示:

asyncfunctionpreviewZipFile(path){constfileName=fileNameEle.value;//获取文件名constresponse=awaitrequest.get(`unzip/${fileName}/entry?path=${path}`);if(response.data&&response.data.status==="success"){const{entryData}=response.data;constentryBuffer=toArrayBuffer(entryData.data);constblob=newBlob([entryBuffer]);//使用URL.createObjectURL或blob.text()读取文件信息}}

由于完整的示例代码内容比较多,阿宝哥就不放具体的代码了。感兴趣的小伙伴,可以访问以下地址浏览示例代码。

/semlinker/3bb9634f4e4ec7b6ab4008a688583115

注意:以上代码仅供参考,请根据实际业务进行调整。

四、总结

本文阿宝哥介绍了在线解压 ZIP 文件的两种方案,在实际项目中,建议使用服务器解压的方案。这样不仅可以解决浏览器的兼容性问题,而且也可以解决大文件在线解压的问题,同时也方便后期扩展支持其它的压缩格式。

五、参考资源

维基百科 ZIP 格式

Limitations of JSZip

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。