jszip压缩文件夹教你JavaScript 在线解压 ZIP 文件




jszip压缩文件夹教你JavaScript 在线解压 ZIP 文件

2022-07-20 19:30:50 网络知识 官方管理员

相信大家对ZIP文件都不会陌生,当你要打开本地的ZIP文件时,你就需要先安装支持解压ZIP文件的解压软件。但如果预解压的ZIP文件在服务器上,我们应该如何处理呢?最简单的一种方案就是把文件下载到本地,然后使用支持ZIP格式的解压软件进行解压。那么能不能在线解压ZIP文件呢?答案是可以的,接下来阿宝哥将介绍浏览器解压和服务器解压两种在线解压ZIP文件的方案。

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

一、ZIP格式简介

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

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

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

关注「全栈修仙之路」阅读阿宝哥原创的4本免费电子书(累计下载3万)及11篇Vue3进阶系列教程。

二、浏览器解压方案

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

jszip压缩文件夹(教你JavaScript在线解压ZIP文件)(1)

其实有了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文件的功能已经介绍完了,我们来看一下以上示例的运行结果:

jszip压缩文件夹(教你JavaScript在线解压ZIP文件)(2)

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

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

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

三、服务器解压方案

服务器解压方案就是允许用户通过文件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_HOME和fileName获得文件的最终路径,然后使用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;}}

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

jszip压缩文件夹(教你JavaScript在线解压ZIP文件)(3)

现在我们已经实现根据文件名解压指定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()读取文件信息}}

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

gist.github.com/semlinker/3…

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

四、总结

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

发表评论:

最近发表
网站分类
标签列表