使用 java 原生 api 实现简单压缩
关键类:ZipOutputStream,ZipEntry
- ZipOutputStream 压缩流,用与将数据写入 zip 中
- ZipEntry 描述了 zip 文件的内部结构,ZipEntry 中存放着文件路径与文件数据的映射
eg: /mm/hello.java --> hello.java文件 ; /mm/nn/world.js --> world.js文件
因此在解压的时候就可以通过读取 ZipEntry 创建相对应的路径,再将文件数据写入对应路径,即可完成解压- zipOutputStream.putNextEntry(zipEntry) ,zipInputStream.getNextEntry()
- java 原生 zip api 不支持中文,所以压缩中又出现中文字符的文件名时会出现乱码的情况
- 解决方法: 下载 apache-ant-zip.jar 使用 apache提供的 ZipOutputStream , ZipEntry,zipOutputStream.setEncoding("");
示例代码😜:
/**
* java zip解压缩工具
* zip文件中可以存在空目录,可以设置是否包含顶层目录 eg: /mm: (1.txt,nn/) --> xx.zip: (1.txt,mm/) (没有顶层目录mm)
* @Description: TODO
* @CreateTime: 2016年11月5日 下午5:31:41
* @author: 蕪園樓主
* @version V1.0
*/
public final class ZipUtil {
private static byte[] buffer = new byte[1024 * 10]; //压缩缓冲
private ZipUtil() {
// empty
}
/**
* 压缩一个目录
* @Description: TODO
* @CreateTime: 2016年11月5日 下午5:29:15
* @author: 蕪園樓主
* @param filePath 源文件绝对路径
* @param zipFilePath zip文件绝对路径 (如果此处是目录名,则默认使用源文件名作为zip文件名)
* @param keepTopDir zip文件中是否包含顶层目录
* @return
*/
public static File zipDir(String filePath, String zipFilePath, boolean keepTopDir) {
File target = null;
File source = new File(filePath);
if(!source.exists() || !source.isDirectory()){
return null;
}
target = new File(zipFilePath);
if(target.isDirectory()){ //如果是目录,则压缩文件名为源文件名
target = new File(target.getAbsolutePath(), source.getName().concat(".zip"));
}else if (target.isFile()) {
target.delete(); // 删除旧的文件
}else{ //不存在
if(! target.getName().matches(".+\\..+$")){ //一个不存在的目录,以是否有扩展名为判断是否是一个目录
target.mkdirs();
target = new File(target.getAbsolutePath(), source.getName().concat(".zip"));
}else{ //一个不存在的文件
if(! target.getParentFile().exists()){
target.getParentFile().mkdirs();
}
}
}
FileOutputStream fos = null;
ZipOutputStream zos = null;
try {
fos = new FileOutputStream(target);
zos = new ZipOutputStream(new BufferedOutputStream(fos));
// 添加对应的文件Entry
addEntry("", source, zos, keepTopDir);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
//关闭流
}
return target;
}
/**
* 压缩一个文件
* @Description: TODO
* @CreateTime: 2016年11月5日 下午5:27:10
* @author: 蕪園樓主
* @param filePath 源文件绝对路径
* @param zipDir zip文件父目录
* @param zipName zip文件名 (如果此处是目录名,则默认使用源文件名作为zip文件名)
* @return
*/
public static File zipFile(String filePath, String zipFilePath) throws Exception{
File target = null;
File source = new File(filePath);
if(!source.exists() || !source.isFile()){
throw new Exception("压缩源目录无效");
}
target = new File(zipFilePath);
if(target.isDirectory()){ //如果是目录,则压缩文件名为源文件名
target = new File(target.getAbsolutePath(), source.getName().concat(".zip"));
}else if (target.isFile()) {
target.delete(); // 删除旧的文件
}else{ //不存在
if(! target.getName().matches("\\..+$")){ //一个不存在的目录,以是否有扩展名为判断是否是一个目录
target.mkdirs();
target = new File(target.getAbsolutePath(), source.getName().concat(".zip"));
}else{
target.getParentFile().mkdirs();
}
}
FileOutputStream fos = null;
ZipOutputStream zos = null;
try {
fos = new FileOutputStream(target);
zos = new ZipOutputStream(new BufferedOutputStream(fos));
zos.setEncoding("utf-8"); //设置编码
// 添加对应的文件Entry
addEntry("", source, zos, true);
} catch (IOException e) {
throw new Exception("压缩文件内部结构构建失败");
} finally {
closeQuietly(zos, fos);
}
return target;
}
/**
*
* @Description: 压缩文件,构建压缩内部文件结构
* @CreateTime: 2016年11月5日 下午5:23:48
* @author: 蕪園樓主
* @param base
* @param source 被压缩源文件
* @param zos 压缩输出流
* @param dir 压缩文件中是否包含顶层目录
* @throws IOException
*/
private static void addEntry(String base, File source, ZipOutputStream zos, boolean dir)
throws IOException {
String entry = (dir) ? (base.concat(source.getName())) : base;
if (source.isDirectory()) {
File[] files = source.listFiles();
if(files == null || files.length < 1){ //空目录
zos.putNextEntry(new ZipEntry(entry));
zos.closeEntry();
return;
}
for (File file : files) {
// 递归列出目录下的所有文件,添加文件Entry
addEntry(((dir)?(entry.concat("/")):entry), file, zos, true);
}
} else {
FileInputStream fis = null;
BufferedInputStream bis = null;
try {
fis = new FileInputStream(source);
bis = new BufferedInputStream(fis, buffer.length);
int read = 0;
zos.putNextEntry(new ZipEntry(entry));
while ((read = bis.read(buffer, 0, buffer.length)) != -1) {
zos.write(buffer, 0, read);
}
zos.closeEntry();
}
finally {
//关闭流
}
}
}
}