最近碰到个需要下载zip压缩包的需求,于是我在网上找了下别人写好的zip工具类。但找了好多篇博客,总是发现有bug。因此就自己来写了个工具类。
这个工具类的功能为:
- (1)可以压缩文件,也可以压缩文件夹
- (5)代码中提供了2个压缩文件的方法,一个的输入参数为文件夹路径,一个为文件列表,可根据实际需求选择方法。
- (4)可以选择是否保留原来的目录结构,如果不保留,所有文件跑压缩包根目录去了,且空文件夹直接舍弃。注意:如果不保留文件原来目录结构,在碰到文件名相同的文件时,会压缩失败。
- (3)碰到空的文件夹,也可以压缩
- (2)同时支持压缩多级文件夹,工具内部做了递归处理
1.使用java自带解压缩,Java自带的java.util.zip,有中文乱码的bug
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
/**
* @description :ZipUtil
* @author :liu
* @since :2021/12/3 14:16
*/
public class ZipUtil {
/**
* 缓冲大小
*/
private static int BUFFERSIZE = 2 << 9;
/**
* 文件压缩
* @param zipFileName
* @param inputFile
* @throws Exception
*/
public static void zip(String zipFileName, String inputFile) throws Exception {
zip(zipFileName, new File(inputFile));
}
/**
* 压缩文件
* @param zipFileName 生成文件名
* @param inputFile 需要压缩文件流
* @throws Exception
*/
public static void zip(String zipFileName, File inputFile) throws Exception {
ZipOutputStream out = new ZipOutputStream(new FileOutputStream(
zipFileName));
zip(out, inputFile, inputFile.getName());
out.flush();
out.close();
}
/**
* 解压zip文件
* @param zipFileName
* @param outputDirectory 解压到的文件夹
* @throws Exception
*/
public static void unzip(String zipFileName, String outputDirectory)
throws Exception {
ZipInputStream in = new ZipInputStream(new FileInputStream(zipFileName));
ZipEntry z;
while ((z = in.getNextEntry()) != null)
{
System.out.println("unziping " + z.getName());
if (z.isDirectory()) {
String name = z.getName();
name = name.substring(0, name.length() - 1);
File f = new File(outputDirectory + File.separator + name);
if (!f.getParentFile().exists()){
f.getParentFile().mkdirs();
}
if (!f.exists()){
f.mkdir();
}
System.out.println("mkdir " + outputDirectory + File.separator
+ name);
}
else {
File f = new File(outputDirectory + File.separator
+ z.getName());
if (!f.getParentFile().exists()){
f.getParentFile().mkdirs();
}
if (!f.exists()){
f.createNewFile();
}
FileOutputStream out = new FileOutputStream(f);
//此处注释部分第一次压缩后文件打不开,后来又可以
// int b;
// while ((b = in.read()) != -1) {
// out.write(b);
// }
//下面部分代码替换了上面注释部分的代码压缩正常
byte[] bytes = new byte[BUFFERSIZE];
int b = -1;
while ((b = in.read(bytes)) != -1) {
out.write(bytes,0,b);
}
out.close();
}
}
in.close();
}
/**
* 文件压缩
* @param out zip输出流
* @param f 需要压缩的文件,可以为文件夹也可以为文件
* @param base
* @throws Exception
*/
public static void zip(ZipOutputStream out, File f, String base) throws Exception {
if (f.isDirectory()) {
File[] fl = f.listFiles();
if (null == fl || fl.length == 0) {
out.putNextEntry(new ZipEntry(base+File.separator));
out.closeEntry();
}else{
for (int i = 0; i < fl.length; i++) {
zip(out, fl[i], base+ File.separator + fl[i].getName());
}
}
} else {
out.putNextEntry(new ZipEntry(base));
FileInputStream in = new FileInputStream(f);
//此处注释部分第一次压缩后文件打不开,后来又可以
// int b;
// while ((b = in.read()) != -1) {
// out.write(b);
// }
//下面部分代码替换了上面注释部分的代码压缩正常
byte[] bytes = new byte[BUFFERSIZE];
int b = -1;
while ((b = in.read(bytes)) != -1) {
out.write(bytes,0,b);
}
out.closeEntry();
in.close();
}
}
public static void main(String[] args) {
String sourceDir = "C:\\Users\\liu\\Desktop\\头像";
String targetDir="C:\\Users\\liu\\Desktop\\";
File f =new File(sourceDir);
String zipName = targetDir+"测试.zip";
try {
zip(zipName,f);
unzip(zipName,"C:\\Users\\liu\\Desktop\\test");
} catch (Exception e) {
e.printStackTrace();
}
}
}
2.将文件追加到指定zip中,用org.apache.ant的压缩工具,支持编码,不能直接追加,先解压,再压缩
添加pom包
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant</artifactId>
<version>1.10.12</version>
</dependency>
有如下目录结构: D:\测试\文档.zip D:\测试\说明.pdf 把“说明.pdf”添加到“文档.zip”里面,生成一个新压缩包“文档(新).zip”。
/**
* 压缩
*
* @param paths
* @param fileName
*/
public static void zip(List<String> paths, String fileName) {
ZipOutputStream zos = null;
try {
zos = new ZipOutputStream(new FileOutputStream(fileName));
for (String filePath : paths) {
// 递归压缩文件
File file = new File(filePath);
String relativePath = file.getName();
if (file.isDirectory()) {
relativePath += File.separator;
}
zipFile(file, relativePath, zos);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (zos != null) {
zos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
public static void zipFile(File file, String relativePath, ZipOutputStream zos) {
InputStream is = null;
try {
if (!file.isDirectory()) {
ZipEntry zp = new ZipEntry(relativePath);
zos.putNextEntry(zp);
is = new FileInputStream(file);
byte[] buffer = new byte[BUFFERSIZE];
int length = 0;
while ((length = is.read(buffer)) >= 0) {
zos.write(buffer, 0, length);
}
zos.setEncoding("gbk");//解决文件名中文乱码
zos.flush();
zos.closeEntry();
} else {
String tempPath = null;
for (File f : Objects.requireNonNull(file.listFiles())) {
tempPath = relativePath + f.getName();
if (f.isDirectory()) {
tempPath += File.separator;
}
zipFile(f, tempPath, zos);
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 解压缩
*
* @param fileName
* @param path
*/
public static List<String> unzip2(String fileName, String path) {
FileOutputStream fos = null;
InputStream is = null;
List<String> filePaths = new ArrayList<String>();
try {
ZipFile zf = new ZipFile(new File(fileName), "UTF-8");
Enumeration<?> en = zf.getEntries();
while (en.hasMoreElements()) {
ZipEntry zn = (ZipEntry) en.nextElement();
if (!zn.isDirectory()) {
is = zf.getInputStream(zn);
File f = new File(path + zn.getName());
File file = f.getParentFile();
file.mkdirs();
fos = new FileOutputStream(path + zn.getName());
int len = 0;
byte bufer[] = new byte[BUFFERSIZE];
while (-1 != (len = is.read(bufer))) {
fos.write(bufer, 0, len);
}
fos.close();
filePaths.add(path + zn.getName());
}
}
} catch (ZipException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (null != is) {
is.close();
}
if (null != fos) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return filePaths;
}
/**
* 有如下目录结构:
* D:\测试\文档.zip
* D:\测试\说明.pdf
* 把“说明.pdf”添加到“文档.zip”里面,生成一个新压缩包“文档(新).zip”。
* @param args
*/
public static void main(String[] args) {
//解压
List<String> files = ZipUtil2.unzip2("D:/测试/文档.zip", "D:/测试/");
//集合添加文件
files.add("D:/测试/说明.pdf");
//压缩
ZipUtil2.zip(files,"D:/测试/文档(新).zip");
//保留说明.pdf
files.remove(files.size()-1);
//删除上面解压出来的文件
for(String f : files){
File file = new File(f);
if(file.exists()){
file.delete();
}
}
}
3.可以压缩文件,也可以压缩文件夹,且保留原有目录,用org.apache.ant的压缩工具,支持编码
private static final int BUFFERSIZE = 2 << 9;
/**
* 压缩成ZIP 方法1
* @param srcDir 压缩文件夹路径
* @param out 压缩文件输出流
* @param KeepDirStructure 是否保留原来的目录结构,true:保留目录结构;false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)
* @throws RuntimeException
*/
public static void toZip(String srcDir, OutputStream out, boolean KeepDirStructure) throws RuntimeException{
long start = System.currentTimeMillis();
ZipOutputStream zos = null ;
try {
zos = new ZipOutputStream(out);
File sourceFile = new File(srcDir);
compress(sourceFile,zos,sourceFile.getName(),KeepDirStructure);
long end = System.currentTimeMillis();
System.out.println("压缩完成,耗时:" + (end - start) +" ms");
} catch (Exception e) {
throw new RuntimeException("zip error from ZipUtils",e);
}finally{
if(zos != null){
try {
zos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 压缩成ZIP 方法2
* @param srcFiles 需要压缩的文件列表
* @param out 压缩文件输出流
* @throws RuntimeException
*/
public static void toZip(List<File> srcFiles , OutputStream out)throws RuntimeException {
long start = System.currentTimeMillis();
ZipOutputStream zos = null ;
try {
zos = new ZipOutputStream(out);
for (File srcFile : srcFiles) {
byte[] buf = new byte[BUFFERSIZE];
zos.putNextEntry(new ZipEntry(srcFile.getName()));
int len;
FileInputStream in = new FileInputStream(srcFile);
while ((len = in.read(buf)) != -1){
zos.write(buf, 0, len);
}
zos.closeEntry();
in.close();
}
long end = System.currentTimeMillis();
System.out.println("压缩完成,耗时:" + (end - start) +" ms");
} catch (Exception e) {
throw new RuntimeException("zip error from ZipUtils",e);
}finally{
if(zos != null){
try {
zos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 递归压缩方法
* @param sourceFile 源文件
* @param zos zip输出流
* @param name 压缩后的名称
* @param KeepDirStructure 是否保留原来的目录结构,true:保留目录结构;false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)
*/
public static void compress(File sourceFile, ZipOutputStream zos, String name, boolean KeepDirStructure){
try {
byte[] buf = new byte[BUFFERSIZE];
if(sourceFile.isFile()){
// 向zip输出流中添加一个zip实体,构造器中name为zip实体的文件的名字
zos.putNextEntry(new ZipEntry(name));
// copy文件到zip输出流中
int len;
FileInputStream in = new FileInputStream(sourceFile);
while ((len = in.read(buf)) != -1){
zos.write(buf, 0, len);
}
// Complete the entry
zos.setEncoding("utf-8");
zos.closeEntry();
in.close();
} else {
File[] listFiles = sourceFile.listFiles();
if(listFiles == null || listFiles.length == 0){
// 需要保留原来的文件结构时,需要对空文件夹进行处理
if(KeepDirStructure){
// 空文件夹的处理
zos.putNextEntry(new ZipEntry(name + "/"));
zos.setEncoding("utf-8");
// 没有文件,不需要文件的copy
zos.closeEntry();
}
}else {
for (File file : listFiles) {
// 判断是否需要保留原来的文件结构
if (KeepDirStructure) {
// 注意:file.getName()前面需要带上父文件夹的名字加一斜杠,
// 不然最后压缩包中就不能保留原来的文件结构,即:所有文件都跑到压缩包根目录下了
compress(file, zos, name + "/" + file.getName(),KeepDirStructure);
} else {
compress(file, zos, file.getName(),KeepDirStructure);
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
/** 测试压缩方法1 */
FileOutputStream fos1 = new FileOutputStream(new File("C:\\Users\\liu\\Desktop\\测试2.zip"));
ZipUtil2.toZip("C:\\Users\\liu\\Desktop\\头像", fos1,true);
/** 测试压缩方法2 */
List<File> fileList = new ArrayList<>();
fileList.add(new File("C:\\Users\\liu\\Desktop\\头像\\微信图片_20211117095343.jpg"));
fileList.add(new File("C:\\Users\\liu\\Desktop\\头像\\微信图片_20211117095337.jpg"));
FileOutputStream fos2 = new FileOutputStream(new File("C:\\Users\\liu\\Desktop\\测试1.zip"));
ZipUtil2.toZip(fileList, fos2);
}