当前位置: 首页 > 科技观察

JavaScript如何压缩目录并上传?

时间:2023-03-12 02:36:36 科技观察

本文转载自微信公众号《全栈修仙之路》,作者阿宝哥。转载本文请联系全栈修真之路公众号。在日常工作中,文件上传是一个很常见的功能。在上传文件的时候,我们可以通过设置multiple属性来选择上传单个文件或者上传多个文件。在这篇文章中,阿宝哥将介绍如何上传目录,以及如何压缩目录并上传。压缩目录的功能是通过JSZip库实现的。使用这个库还可以实现在线预览ZIP文件的功能。有兴趣的朋友可以在线阅读JavaScript如何解压ZIP文件?本文。下面先介绍一下如何实现压缩目录并上传的功能。1.浏览器端1.1选择目录在浏览器端,必须实现压缩目录和上传的功能。首先,我们需要实现选择目录的功能。要实现这个功能,我们可以直接使用HTMLInputElement元素的webkitdirectory属性:当阿宝哥选择useAxios目录时,会出现如下确认框:点击上传按钮后,我们可以得到文件列表。列表中的文件对象包含一个webkitRelativePath属性,用于指示当前文件的相对路径。在进行目录压缩时,我们会用到这个属性。虽然通过webkitdirectory属性可以很方便的实现选择目录的功能,但是在实际项目中我们还需要考虑其兼容性。例如IE11以下版本不支持该属性,其他浏览器的兼容性如下图:(图片来源——https://caniuse.com/?search=webkitdirectory)1.2如何解压一个JavaScriptZIP文件中的在线压缩目录?阿宝哥在这篇文章中介绍了如何在浏览器端使用JSZip库实现在线解压ZIP文件的功能。JSZip除了解析ZIP文件之外,该库还可用于创建和编辑ZIP文件。这里宝哥根据JSZip库提供的API封装了一个generateZipFile函数:constzip=newJSZip();for(leti=0;i上传文件js代码constuploadFileEle=document.querySelector("#uploadFile");constuploadOptions={needZip=true};constrequest=axios.create({baseURL:"http://localhost:3000/",timeout:5000,});asyncfunctionuploadFile({needZip}=uploadOptions){if(!uploadFileEle.files.length)return;letfileList=uploadFileEle.files;if(needZip){//用ZIP压缩目录letwebkitRelativePath=fileList[0].webkitRelativePath;letzipFileName=webkitRelativePath.split("/")[0]+".zip";fileList=[awaitgenerateZipFile(zipFileName,fileList)];}uploadFiles({//上传文件列表url:"/upload/multiple",files:fileList,});}uploadFile函数中,如果开启了目录压缩功能,我们会调用generateZipFile函数来生成一个ZIP文件。如果没有,我们就直接调用uploadFiles函数上传目录下的所有文件。当然你也可以过滤文件列表,比如限制文件类型或者文件大小。下面看一下uploadFiles函数的具体实现:functionuploadFiles({url,files,fieldName="file"}){if(!url||!files.length)return;letformData=newFormData();for(leti=0;i{ctx.body="压缩文件目录上传示例(阿宝哥)";});router.post("/upload/multiple",multerUpload.fields([{name:"file",},]),async(ctx,next)=>{ctx.body={status:"success",msg:"文件上传successfully",};});//注册中间件app.use(cors());app.use(router.routes()).use(router.allowedMethods());app.listen(3000,()=>{console.log("appstartingatport3000");});在上面的代码中,我们通过@koa/multer这个中间件处理文件上传,对这个中间件感兴趣的朋友可以自行阅读官方文档。接下来我们继续讨论另一个问题——如何接收目录并按照文件目录结构存储?2.2接收文件目录我们已经知道,当input[type="file"]使用webkitdirectory属性时,返回的File对象的webkitRelativePath属性会存储当前文件相对于当前目录的相对路径:所以当我们在服务端处理文件目录上传功能,我们可以利用这个属性创建相应的目录结构。具体处理逻辑如下:constfse=require("fs-extra");conststorage=multer.diskStorage({destination:asyncfunction(req,file,cb){//useAxios@demo.vue中的@替换为路径分隔符letrelativePath=file.originalname.replace(/@/g,path.sep);letindex=relativePath.lastIndexOf(path.sep);letfileDir=path.join(UPLOAD_DIR,relativePath.substr(0,index));//生成文件路径awaitfse.ensureDir(fileDir);//确保当前目录存在cb(null,fileDir);},filename:function(req,file,cb){letparts=file.originalname.split("@");//拆分路径cb(null,`${parts[parts.length-1]}`);//获取文件名},});为什么originalname文件的原名中包含@符号?这是因为使用useAxios/demo.vue这种路径形式时,无法获取到完整的路径名,只能获取到文件名。为了解决这个问题,阿宝哥在上传文件时,手动将文件相对路径中的/符号替换成@,然后再上传。对应的处理逻辑如下:functionuploadFiles({url,files,fieldName="file"}){if(!url||!files.length)return;letformData=newFormData();for(leti=0;i