Java 如何实现解压缩文件和文件夹
一前言
项目开发中,总会遇到解压缩文件的时候。比如,用户下载多个文件时,服务端可以将多个文件压缩成一个文件(例如xx.zip或xx.rar)。用户上传资料时,允许上传压缩文件,服务端进行解压读取每一个文件。
基于通用性,以下介绍几种解压缩文件的方式,包装成工具类,供平时开发使用。
二压缩文件
压缩文件,顾名思义,即把一个或多个文件压缩成一个文件。压缩也有2种形式,一种是将所有文件压缩到同一目录下,此种方式要注意文件重名覆盖的问题。另一种是按原有文件树结构进行压缩,即压缩后的文件树结构保持不变。
压缩文件操作,会使用到一个类,即ZipOutputStream。
2.1压缩多个文件
此方法将所有文件压缩到同一个目录下。方法传入多个文件列表,和一个最终压缩到的文件路径名。
/** *压缩多个文件,压缩后的所有文件在同一目录下 * *@paramzipFileName压缩后的文件名 *@paramfiles需要压缩的文件列表 *@throwsIOExceptionIO异常 */ publicstaticvoidzipMultipleFiles(StringzipFileName,File...files)throwsIOException{ ZipOutputStreamzipOutputStream=null; try{ //输出流 zipOutputStream=newZipOutputStream(newFileOutputStream(zipFileName)); //遍历每一个文件,进行输出 for(Filefile:files){ zipOutputStream.putNextEntry(newZipEntry(file.getName())); FileInputStreamfileInputStream=newFileInputStream(file); intreadLen; byte[]buffer=newbyte[1024]; while((readLen=fileInputStream.read(buffer))!=-1){ zipOutputStream.write(buffer,0,readLen); } //关闭流 fileInputStream.close(); zipOutputStream.closeEntry(); } }finally{ if(null!=zipOutputStream){ try{ zipOutputStream.close(); }catch(IOExceptionex){ ex.printStackTrace(); } } } }
测试,将D盘下的infp.txt和infp1.txt文件压缩到D盘下,压缩文件名为my.zip。
publicstaticvoidmain(String[]args)throwsException{ zipMultipleFiles("D:/my.zip",newFile("D:/infp.txt"),newFile("D:/infp1.txt")); }
2.2压缩文件或文件树
此方法将文件夹下的所有文件按原有的树形结构压缩到文件中,也支持压缩单个文件。原理也简单,无非就是递归遍历文件树中的每一个文件,进行压缩。有个注意的点每一个文件的写入路径是基于压缩文件位置的相对路径。
packagecom.nobody.zip; importjava.io.*; importjava.util.zip.ZipEntry; importjava.util.zip.ZipInputStream; importjava.util.zip.ZipOutputStream; publicclassZipUtils{ /** *压缩文件或文件夹(包括所有子目录文件) * *@paramsourceFile源文件 *@paramformat格式(zip或rar) *@throwsIOException异常信息 */ publicstaticvoidzipFileTree(FilesourceFile,Stringformat)throwsIOException{ ZipOutputStreamzipOutputStream=null; try{ StringzipFileName; if(sourceFile.isDirectory()){//目录 zipFileName=sourceFile.getParent()+File.separator+sourceFile.getName()+"." +format; }else{//单个文件 zipFileName=sourceFile.getParent() +sourceFile.getName().substring(0,sourceFile.getName().lastIndexOf(".")) +"."+format; } //压缩输出流 zipOutputStream=newZipOutputStream(newFileOutputStream(zipFileName)); zip(sourceFile,zipOutputStream,""); }finally{ if(null!=zipOutputStream){ //关闭流 try{ zipOutputStream.close(); }catch(IOExceptionex){ ex.printStackTrace(); } } } } /** *递归压缩文件 * *@paramfile当前文件 *@paramzipOutputStream压缩输出流 *@paramrelativePath相对路径 *@throwsIOExceptionIO异常 */ privatestaticvoidzip(Filefile,ZipOutputStreamzipOutputStream,StringrelativePath) throwsIOException{ FileInputStreamfileInputStream=null; try{ if(file.isDirectory()){//当前为文件夹 //当前文件夹下的所有文件 File[]list=file.listFiles(); if(null!=list){ //计算当前的相对路径 relativePath+=(relativePath.length()==0?"":"/")+file.getName(); //递归压缩每个文件 for(Filef:list){ zip(f,zipOutputStream,relativePath); } } }else{//压缩文件 //计算文件的相对路径 relativePath+=(relativePath.length()==0?"":"/")+file.getName(); //写入单个文件 zipOutputStream.putNextEntry(newZipEntry(relativePath)); fileInputStream=newFileInputStream(file); intreadLen; byte[]buffer=newbyte[1024]; while((readLen=fileInputStream.read(buffer))!=-1){ zipOutputStream.write(buffer,0,readLen); } zipOutputStream.closeEntry(); } }finally{ //关闭流 if(fileInputStream!=null){ try{ fileInputStream.close(); }catch(IOExceptionex){ ex.printStackTrace(); } } } } publicstaticvoidmain(String[]args)throwsException{ Stringpath="D:/test"; Stringformat="zip"; zipFileTree(newFile(path),format); } }
上例将test目录下的所有文件压缩到同一目录下的test.zip文件中。
2.3借助文件访问器压缩
还有一种更简单的方式,我们不自己写递归遍历。借助Java原生类,SimpleFileVisitor,它提供了几个访问文件的方法,其中有个方法visitFile,对于文件树中的每一个文件(文件夹除外),都会调用这个方法。我们只要写一个类继承SimpleFileVisitor,然后重写visitFile方法,实现将每一个文件写入到压缩文件中即可。
当然,除了visitFile方法,它里面还有preVisitDirectory,postVisitDirectory,visitFileFailed等方法,通过方法名大家也猜出什么意思了。
packagecom.nobody.zip; importjava.io.FileOutputStream; importjava.io.IOException; importjava.nio.file.*; importjava.nio.file.attribute.BasicFileAttributes; importjava.util.zip.ZipEntry; importjava.util.zip.ZipOutputStream; /** *@Description *@AuthorMr.nobody *@Date2021/3/8 *@Version1.0.0 */ publicclassZipFileTreeextendsSimpleFileVisitor{ //zip输出流 privateZipOutputStreamzipOutputStream; //源目录 privatePathsourcePath; publicZipFileTree(){} /** *压缩目录以及所有子目录文件 * *@paramsourceDir源目录 */ publicvoidzipFile(StringsourceDir)throwsIOException{ try{ //压缩后的文件和源目录在同一目录下 StringzipFileName=sourceDir+".zip"; this.zipOutputStream=newZipOutputStream(newFileOutputStream(zipFileName)); this.sourcePath=Paths.get(sourceDir); //开始遍历文件树 Files.walkFileTree(sourcePath,this); }finally{ //关闭流 if(null!=zipOutputStream){ zipOutputStream.close(); } } } //遍历到的每一个文件都会执行此方法 @Override publicFileVisitResultvisitFile(Pathfile,BasicFileAttributesattributes)throwsIOException{ //取相对路径 PathtargetFile=sourcePath.relativize(file); //写入单个文件 zipOutputStream.putNextEntry(newZipEntry(targetFile.toString())); byte[]bytes=Files.readAllBytes(file); zipOutputStream.write(bytes,0,bytes.length); zipOutputStream.closeEntry(); //继续遍历 returnFileVisitResult.CONTINUE; } //遍历每一个目录时都会调用的方法 @Override publicFileVisitResultpreVisitDirectory(Pathdir,BasicFileAttributesattrs) throwsIOException{ returnsuper.preVisitDirectory(dir,attrs); } //遍历完一个目录下的所有文件后,再调用这个目录的方法 @Override publicFileVisitResultpostVisitDirectory(Pathdir,IOExceptionexc)throwsIOException{ returnsuper.postVisitDirectory(dir,exc); } //遍历文件失败后调用的方法 @Override publicFileVisitResultvisitFileFailed(Pathfile,IOExceptionexc)throwsIOException{ returnsuper.visitFileFailed(file,exc); } publicstaticvoidmain(String[]args)throwsIOException{ //需要压缩源目录 StringsourceDir="D:/test"; //压缩 newZipFileTree().zipFile(sourceDir); } }
三解压文件
解压压缩包,借助ZipInputStream类,可以读取到压缩包中的每一个文件,然后根据读取到的文件属性,写入到相应路径下即可。对于解压压缩包中是文件树的结构,每读取到一个文件后,如果是多层路径下的文件,需要先创建父目录,再写入文件流。
packagecom.nobody.zip; importjava.io.*; importjava.util.zip.ZipEntry; importjava.util.zip.ZipInputStream; importjava.util.zip.ZipOutputStream; /** *@Description解压缩文件工具类 *@AuthorMr.nobody *@Date2021/3/8 *@Version1.0.0 */ publicclassZipUtils{ /** *解压 * *@paramzipFilePath带解压文件 *@paramdesDirectory解压到的目录 *@throwsException */ publicstaticvoidunzip(StringzipFilePath,StringdesDirectory)throwsException{ FiledesDir=newFile(desDirectory); if(!desDir.exists()){ booleanmkdirSuccess=desDir.mkdir(); if(!mkdirSuccess){ thrownewException("创建解压目标文件夹失败"); } } //读入流 ZipInputStreamzipInputStream=newZipInputStream(newFileInputStream(zipFilePath)); //遍历每一个文件 ZipEntryzipEntry=zipInputStream.getNextEntry(); while(zipEntry!=null){ if(zipEntry.isDirectory()){//文件夹 StringunzipFilePath=desDirectory+File.separator+zipEntry.getName(); //直接创建 mkdir(newFile(unzipFilePath)); }else{//文件 StringunzipFilePath=desDirectory+File.separator+zipEntry.getName(); Filefile=newFile(unzipFilePath); //创建父目录 mkdir(file.getParentFile()); //写出文件流 BufferedOutputStreambufferedOutputStream= newBufferedOutputStream(newFileOutputStream(unzipFilePath)); byte[]bytes=newbyte[1024]; intreadLen; while((readLen=zipInputStream.read(bytes))!=-1){ bufferedOutputStream.write(bytes,0,readLen); } bufferedOutputStream.close(); } zipInputStream.closeEntry(); zipEntry=zipInputStream.getNextEntry(); } zipInputStream.close(); } //如果父目录不存在则创建 privatestaticvoidmkdir(Filefile){ if(null==file||file.exists()){ return; } mkdir(file.getParentFile()); file.mkdir(); } publicstaticvoidmain(String[]args)throwsException{ StringzipFilePath="D:/test.zip"; StringdesDirectory="D:/a"; unzip(zipFilePath,desDirectory); } }
四总结
在解压缩文件过程中,主要是对流的读取操作,注意进行异常处理,以及关闭流。
web应用中,通过接口可以实现文件上传下载,对应的我们只要把压缩后的文件,写入到response.getOutputStream()输出流即可。
解压缩文件时,注意空文件夹的处理。
此演示项目已上传到Github,如有需要可自行下载,欢迎Star。https://github.com/LucioChn/common-utils
以上就是Java如何实现解压缩文件和文件夹的详细内容,更多关于Java解压缩文件和文件夹的资料请关注毛票票其它相关文章!
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。