1. 解压问题
360压缩文件
使用jdk API 读取压缩文件后解压,报错 java.lang.IllegalArgumentException:MALFORMED
如果是好压压缩文件,使用jdk API 读取压缩文件后能正常解压
网上解决方法
定位到 ZipEntry entry = (ZipEntry) entries.nextElement();
java.util.zip.ZipCoder 第58行
if (isUTF8 && cd instanceof ArrayDecoder) {
int clen = ((ArrayDecoder)cd).decode(ba, 0, length, ca);
if (clen == -1) // malformed
throw new IllegalArgumentException("MALFORMED");
return new String(ca, 0, clen);
}
Windows 压缩的时候使用的是系统的编码 GB2312,而 Mac 系统默认的编码是 UTF-8,于是出现了乱码。
ZipFile zipFile = new ZipFile(zipFileName, Charset.forName("GBK"));
注意:上面虽然解决了,但是 ZipInputStream 目录有中文,也会乱码 无法解压缩文件
2. jdk中api压缩解压
package com.yl.code.util;
import lombok.extern.slf4j.Slf4j;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipOutputStream;
/**
* ZipUtil工具类
*
* @author liuxb
* @date 2022/7/28 8:31
*/
@Slf4j
public class ZipUtil {
private static final int BUFFER_SIZE = 2 * 1024;
/**
* 压缩成ZIP
*
* @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();
try (ZipOutputStream zos = new ZipOutputStream(out);) {
File sourceFile = new File(srcDir);
compress(sourceFile, zos, sourceFile.getName(), keepDirStructure);
long end = System.currentTimeMillis();
log.info("------------压缩完成,耗时: " + (end - start) + "ms");
} catch (Exception e) {
throw new RuntimeException("zip error", e);
}
}
/**
* 递归压缩方法
*
* @param sourceFile 源文件
* @param zos zip输出流
* @param name 压缩后的名称
* @param keepDirStructure 是否保留原来的目录结构, true:保留目录结构 false:所有文件跑到压缩包根目录下(注意:不保留目录结构可能会出现同名文件,会压缩失败)
* @throws Exception
*/
private static void compress(File sourceFile, ZipOutputStream zos, String name, boolean keepDirStructure) throws Exception {
byte[] buf = new byte[BUFFER_SIZE];
if (sourceFile.isFile()) {
// 向zip输出流中添加一个zip实体,构造器中name为zip实体的文件的名字
zos.putNextEntry(new ZipEntry(name));
// copy 文件到zip输出流中
int len;
try (FileInputStream fis = new FileInputStream(sourceFile);) {
while ((len = fis.read(buf)) != -1) {
zos.write(buf, 0, len);
}
zos.closeEntry();
} catch (IOException e) {
throw new RuntimeException("读取异常", e);
}
} else {
File[] listFiles = sourceFile.listFiles();
if (listFiles == null || listFiles.length == 0) {
// 需要保留原来的文件结构时,需要对空文件夹进行处理
if (keepDirStructure) {
zos.putNextEntry(new ZipEntry(name + "/"));
// 没有文件,不需要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);
}
}
}
}
}
/**
* 解压
*
* @param zipFilePath zip压缩文件路径
* @param descDir 目录
*/
public static void unzip(String zipFilePath, String descDir) {
try {
File descFile = new File(descDir);
if (!descFile.exists()) {
descFile.mkdirs();
}
ZipFile zipFile = new ZipFile(zipFilePath);
//列出所有项,包含子目录和子目录内文件
Enumeration<? extends ZipEntry> zs = zipFile.entries();
while (zs.hasMoreElements()) {
ZipEntry zipEntry = zs.nextElement();
log.info(zipEntry.getName());
if (!zipEntry.isDirectory()) {
InputStream in = zipFile.getInputStream(zipEntry);
OutputStream os = new FileOutputStream(descDir + File.separator + zipEntry.getName());
byte[] data = new byte[1024];
int len = -1;
while ((len = in.read(data)) != -1) {
os.write(data, 0, len);
}
os.flush();
os.close();
in.close();
} else {
new File(descDir + File.separator + zipEntry.getName()).mkdirs();
}
}
zipFile.close();
log.info("解压完成...");
} catch (IOException e) {
log.error(zipFilePath + " 解压失败...", e);
}
}
/**
* 删除文件夹
*
* @param dirPath 文件夹路径及名称 如c:/test
*/
public static void delFolder(String dirPath) {
try {
//删除完里面所有内容
delAllFile(dirPath);
log.info("删除{}内所有文件及子目录文件", dirPath);
File myFilePath = new File(dirPath);
myFilePath.delete(); //删除空文件夹
log.info("删除目录: {}", dirPath);
} catch (Exception e) {
log.info("删除文件夹fail!", e);
}
}
/**
* 删除文件夹里面的所有文件 (不删除最外层目录)
*
* @param path 文件夹路径 如 c:/test/
*/
public static void delAllFile(String path) {
File file = new File(path);
if (!file.exists()) {
return;
}
if (!file.isDirectory()) {
return;
}
String[] tempList = file.list();
File temp = null;
for (int i = 0; i < tempList.length; i++) {
if (path.endsWith(File.separator)) {
temp = new File(path + tempList[i]);
} else {
temp = new File(path + File.separator + tempList[i]);
}
if (temp.isFile()) {
temp.delete();
}
if (temp.isDirectory()) {
//先删除文件夹里面的文件
delAllFile(path + "/" + tempList[i]);
//再删除空文件夹
delFolder(path + "/" + tempList[i]);
}
}
}
/**
* 获取给定路径内所有是文件的绝地地址列表
*
* @param dir 路径
* @return 遍历的路径集合
*/
public static List<String> getFiles(String dir) {
List<String> lstFiles = new ArrayList<String>();
File file = new File(dir);
if (!file.exists()) {
return lstFiles;
}
File[] files = file.listFiles();
for (File f : files) {
if (f.isDirectory()) {
lstFiles.addAll(getFiles(f.getAbsolutePath()));
} else {
lstFiles.add(f.getAbsolutePath());
}
}
return lstFiles;
}
}
3. 使用commons-compress解决jdk中zip压缩解压问题
pom依赖
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.13</version>
</dependency>
封装工具类
package com.yl.code.util;
import com.cmbchina.base.exception.BizException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.compress.archivers.zip.Zip64Mode;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
import org.apache.commons.compress.archivers.zip.ZipFile;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
/**
* zip压缩解压工具类
*
* @author liuxb
* @date 2022/7/22 8:24
*/
@Slf4j
public class CompressUtil {
/**
* 压缩文件,支持中文,多个文件压缩
*
* @param files
* @param zipFilePath
*/
public static void compress(File[] files, String zipFilePath) {
if (files != null && files.length > 0) {
File zipFile = new File(zipFilePath);
log.info("{} 开始压缩...", zipFilePath);
try (ZipArchiveOutputStream zaos = new ZipArchiveOutputStream(zipFile)) {
zaos.setUseZip64(Zip64Mode.AsNeeded);
for (File file : files) {
if (file != null) {
ZipArchiveEntry zipArchiveEntry = new ZipArchiveEntry(file, file.getName());
zaos.putArchiveEntry(zipArchiveEntry);
}
try (InputStream in = new BufferedInputStream(new FileInputStream(file))) {
byte[] buffer = new byte[1024 * 10];
int len = -1;
while ((len = in.read(buffer)) != -1) {
zaos.write(buffer, 0, len);
}
}
}
log.info("{} 压缩完成...", zipFilePath);
zaos.closeArchiveEntry();
zaos.finish();
} catch (IOException e) {
throw new BizException("压缩异常", e);
}
}
}
/**
* 解压
*
* @param zipFilePath
*/
public static void decompress(String zipFilePath) {
decompress(zipFilePath, zipFilePath.replace(".zip", ""));
}
/**
* 解压
*
* @param zipFilePath
* @param descDir
*/
public static void decompress(String zipFilePath, String descDir) {
File zipFile = new File(zipFilePath);
File pathFile = new File(descDir);
if (!pathFile.exists()) {
pathFile.mkdirs();
}
log.info("{} 开始解压...", zipFilePath);
try (ZipFile zip = new ZipFile(zipFile)) {
Enumeration<ZipArchiveEntry> entries = zip.getEntries();
while (entries.hasMoreElements()) {
// 遍历获取压缩文件内全部条目,包括子条目中的条目
ZipArchiveEntry entry = entries.nextElement();
String entryName = entry.getName();
try (InputStream in = zip.getInputStream(entry)) {
String outPath = (descDir + "/" + entryName);
// 判断路径是否存在,不存在则创建文件路径
File file = new File(outPath.substring(0, outPath.lastIndexOf('/')));
if (!file.exists()) {
file.mkdirs();
} // 判断文件全路径是否为文件夹,如果是上面已经创建,不需要解压
if (new File(outPath).isDirectory()) {
continue;
}
try (OutputStream out = new FileOutputStream(outPath)) {
byte[] buf = new byte[4 * 1024];
int len = 0;
while ((len = in.read(buf)) >= 0) {
out.write(buf, 0, len);
}
} catch (IOException e) {
throw new BizException("解压失败", e);
}
} catch (IOException e) {
throw new BizException("解压失败", e);
}
}
log.info("{} 解压完毕...", zipFilePath);
} catch (Exception e) {
throw new BizException("解压失败", e);
}
}
/**
* 删除文件夹 * * @param dirPath 文件夹路径及名称 如c:/test
*/
public static void delFolder(String dirPath) {
try {
delAllFile(dirPath);
//删除完里面所有内容
log.info("删除{}内所有文件及子目录文件", dirPath);
File myFilePath = new File(dirPath);
myFilePath.delete();
//删除空文件夹
log.info("删除目录: {}", dirPath);
} catch (Exception e) {
log.error("删除文件夹fail", e);
}
}
/**
* 删除文件夹里面的所有文件 (不删除最外层目录)
*
* @param path 文件夹路径 如 c:/test/
*/
public static void delAllFile(String path) {
File file = new File(path);
if (!file.exists()) {
return;
}
if (!file.isDirectory()) {
return;
}
String[] tempList = file.list();
File temp = null;
for (int i = 0; i < tempList.length; i++) {
if (path.endsWith(File.separator)) {
temp = new File(path + tempList[i]);
} else {
temp = new File(path + File.separator + tempList[i]);
}
if (temp.isFile()) {
temp.delete();
}
if (temp.isDirectory()) {
//先删除文件夹里面的文件
delAllFile(path + "/" + tempList[i]);
//再删除空文件夹
delFolder(path + "/" + tempList[i]);
}
}
}
/**
* 获取给定路径内所有是文件的绝地地址列表
*
* @param dir 路径
* @return 遍历的路径集合
*/
public static List<String> getFiles(String dir) {
List<String> lstFiles = new ArrayList<String>();
File file = new File(dir);
if (!file.exists()) {
return lstFiles;
}
File[] files = file.listFiles();
for (File f : files) {
if (f.isDirectory()) {
lstFiles.addAll(getFiles(f.getAbsolutePath()));
} else {
lstFiles.add(f.getAbsolutePath());
}
}
return lstFiles;
}
}