本文转载自微信公众号《全栈修仙之路》,作者阿宝哥。转载本文请联系全栈修真之路公众号。ZIP文件相信大家都不陌生。要打开本地ZIP文件,需要安装支持ZIP文件解压的解压软件。但是如果预解压的ZIP文件在服务器上,我们该怎么办呢?最简单的解决办法是将文件下载到本地,然后使用支持ZIP格式的解压软件进行解压。那么可以在线解压ZIP文件吗?答案是肯定的。接下来阿宝哥给大家介绍两种在线解压ZIP文件的解决方案:浏览器解压和服务器解压。在介绍两种在线解压ZIP文件的方案之前,我们先简单了解一下ZIP文件格式。一、ZIP格式简介ZIP文件格式是一种用于数据压缩和文档存储的文件格式。ZIP通常使用扩展名“.zip”,其MIME格式为“application/zip”。目前,ZIP格式是几种主流压缩格式之一,其竞争对手包括RAR格式和开源的7z格式。ZIP是一种相当简单的存档格式,它单独压缩每个文件,允许检索单个文件而无需读取额外数据。理论上,这种格式允许不同的文件使用不同的算法。然而,在实践中,大多数ZIP都使用Katz的DEFLATE算法。在简单介绍了ZIP格式之后,阿宝哥首先介绍一下基于JSZip库的浏览器解压解决方案。2.浏览器解压解决方案JSZip是一个用于创建、读取和编辑.zip文件的JavaScript库。这个库支持大多数浏览器。具体兼容性如下图所示:其实借助JSZip库,在浏览器端实现在线解压ZIP文件的功能并不难。因为官方已经为我们提供了解压本地文件、解压远程文件和生成ZIP文件的完整示例。好了,废话少说,下面一步步实现在线解压ZIP文件的功能。2.1定义了工具浏览器端在线解压ZIP文件的功能,可以分为三个小功能:下载ZIP文件、解析ZIP文件、显示ZIP文件。考虑到函数的复用性,包哥将下载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("iterationFnisnotafunctiontype");}letzip;try{zip=awaitJSZip。loadAsync(data);//JSZip来自jszip库zip.forEach(iterationFn);returnzip;}catch(error){thrownewerror();}}}2.2在线解压ZIP文件使用ExeJSZip类的实例,我们可以easily轻松实现在线解压ZIP文件功能:html代码
在线解压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+=`${zipEntry.name}`;});statusEle.innerText="ZIP文件解压成功";fileList.innerHTML=items;}//处理下载进度functionhandleProgress(progressData){const{percent,loaded,total}=progressData;if(loaded===total){statusEle.innerText="文件已下载,正在尝试解压";}}好了,浏览器端如何通过JSZip库实现在线解压ZIP文件的功能已经完成介绍。我们来看看上面例子的运行结果:现在我们可以在线解压Z了IP文件,这时候可能有朋友会问,解压后的文件能预览吗?答案是肯定的,因为JSZip库为我们提供了一个文件API,通过它我们可以读取指定的文件例如使用zip.file("amount.txt").async("arraybuffer"),然后我们就可以执行相应操作,实现文件预览功能。需要注意的是,基于JSZip的解决方案并不完美,它有一些局限性。例如,它不支持解压缩加密的ZIP文件。解压缩较大文件时,IE10以下的浏览器可能会崩溃。除此之外,它还有一些其他的限制,这里就不详细说明了。感兴趣的朋友,可以阅读JSZip的局限性一文中的相关内容。由于浏览器解压方案存在一些不足,尤其是在线解压大文件的情况下,为了解决这个问题,我们可以考虑使用服务器解压方案。3、服务器解压方案服务器解压方案是让用户通过文件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.jsrouter.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代码
在线解压JS代码constfileList=document.querySelector("#fileList");constfileNameEle=document.querySelector("#fileName");constrequest=axios.create({baseURL:"http://localhost:3000/",timeout:10000,});asyncfunctionunzipOnline(){constfileName=fileNameEle.价值;if(!fileName)return;constresponse=awaitrequest.get(`unzip/${fileName}`);if(response.data&&response.data.status==="success"){constentries=response.data.entries;letitems="";entries.forEach((zipEntry)=>{items+=`${zipEntry.name}`;});fileList.innerHTML=items;}}上面的例子运行成功后的结果如下图所示:既然我们已经根据文件名解压了指定的ZIP文件,那么我们可以在指定的路径下预览文件了吗?压缩文件?答案也是可以的。使用zip对象提供的entryData(entry:string|ZipEntry):Promise方法读取指定路径下的文件内容3.3在ZIP文件中预览指定路径下的文件app.jsrouter.get("/unzip/:name/entry",async(ctx)=>{constfileName=ctx.params.name;//ZIP压缩文件名constantPath=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:`Failedtoread${entryPath}filein${fileName}`,};}});上面代码中,我们使用zip.entryData方法读取指定路径的文件内容,返回一个Buffer对象。前端在接收到数据的时候,还需要将接收到的Buffer对象转换成ArrayBuffer对象。对应的处理方法如下:functiontoArrayBuffer(buf){letab=newArrayBuffer(buf.length);letview=newUint8Array(ab);for(leti=0;i