Java解压缩文件
本文我们讨论如何解压缩文件。java核心库提供了一些实用工具,java.util.zip包中可以找到所有压缩和解压相关工具。
1. 压缩文件
包括单个文件、多个文件以及压缩目录。
1.1. 单个文件
首先看个简单操作————压缩单个文件,即把源文件test1.txt压缩为归档文件compressed.zip。
当然首先从磁盘读文件,test1.txt在gradle项目的resouces目录下:
public class ZipDemo {
public static void main(String[] args) throws IOException, URISyntaxException {
String sourceFile = "test1.txt";
URL resource = ZipDemo.class.getClassLoader().getResource(sourceFile);
File fileToZip = new File(resource.toURI());
FileInputStream fis = new FileInputStream(fileToZip);
ZipEntry zipEntry = new ZipEntry(fileToZip.getName());
Path targget = Paths.get(fileToZip.getParent(), "compressed.zip");
FileOutputStream fos = new FileOutputStream(targget.toFile());
ZipOutputStream zipOut = new ZipOutputStream(fos);
zipOut.putNextEntry(zipEntry);
byte[] bytes = new byte[1024];
int length;
while((length = fis.read(bytes)) >= 0) {
zipOut.write(bytes, 0, length);
}
zipOut.close();
fis.close();
fos.close();
}
}
生成的目标归档文件在相同目录下,build中。
1.2. 压缩多个文件
接下来看如何压缩多个文件:
public class ZipMultipleFiles {
public static void main(String[] args) throws IOException {
List<String> srcFiles = Arrays.asList("test1.txt", "test2.txt");
FileOutputStream fos = new FileOutputStream("multiCompressed.zip");
ZipOutputStream zipOut = new ZipOutputStream(fos);
for (String srcFile : srcFiles) {
File fileToZip = new File(srcFile);
FileInputStream fis = new FileInputStream(fileToZip);
ZipEntry zipEntry = new ZipEntry(fileToZip.getName());
zipOut.putNextEntry(zipEntry);
byte[] bytes = new byte[1024];
int length;
while((length = fis.read(bytes)) >= 0) {
zipOut.write(bytes, 0, length);
}
fis.close();
}
zipOut.close();
fos.close();
}
}
1.3. 压缩目录
让我们讨论如何压缩整个目录:
public class ZipDirectory {
public static void main(String[] args) throws IOException {
String sourceDir = "zipTest";
FileOutputStream fos = new FileOutputStream("dirCompressed.zip");
ZipOutputStream zipOut = new ZipOutputStream(fos);
File fileToZip = new File(sourceDir);
zipFile(fileToZip, fileToZip.getName(), zipOut);
zipOut.close();
fos.close();
}
private static void zipFile(File fileToZip, String fileName, ZipOutputStream zipOut) throws IOException {
if (fileToZip.isHidden()) {
return;
}
if (fileToZip.isDirectory()) {
if (fileName.endsWith(File.pathSeparator)) {
fileName = fileName + File.pathSeparator;
}
zipOut.putNextEntry(new ZipEntry(fileName));
zipOut.closeEntry();
File[] children = fileToZip.listFiles();
for (File childFile : children) {
zipFile(childFile, fileName + childFile.getName(), zipOut);
}
return;
}
FileInputStream fis = new FileInputStream(fileToZip);
ZipEntry zipEntry = new ZipEntry(fileName);
zipOut.putNextEntry(zipEntry);
byte[] bytes = new byte[1024];
int length;
while ((length = fis.read(bytes)) >= 0) {
zipOut.write(bytes, 0, length);
}
fis.close();
}
}
可能有子目录,这里需要递归进行迭代。
每次发现目录,在子ZipEntity中增加其名称用于保存层次。
我们也给空目录创建目录项。
2. 解压
解压 compressed.zip 至新的文件夹nuzipTest:
public class UnzipFile {
public static void main(String[] args) throws IOException {
String fileZip = "src/main/resources/unzipTest/compressed.zip";
File destDir = new File("src/main/resources/unzipTest");
byte[] buffer = new byte[1024];
ZipInputStream zis = new ZipInputStream(new FileInputStream(fileZip));
ZipEntry zipEntry = zis.getNextEntry();
while (zipEntry != null) {
File newFile = newFile(destDir, zipEntry);
FileOutputStream fos = new FileOutputStream(newFile);
int len;
while ((len = zis.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
fos.close();
zipEntry = zis.getNextEntry();
}
zis.closeEntry();
zis.close();
}
public static File newFile(File destinationDir, ZipEntry zipEntry) throws IOException {
File destFile = new File(destinationDir, zipEntry.getName());
String destDirPath = destinationDir.getCanonicalPath();
String destFilePath = destFile.getCanonicalPath();
if (!destFilePath.startsWith(destDirPath + File.separator)) {
throw new IOException("Entry is outside of the target dir: " + zipEntry.getName());
}
return destFile;
}
}
另一个关键点是在newFile()方法中防止将文件写入目标文件夹外的文件系统,该问题称为Zip Slip漏洞。
3. 总结
本文介绍如何利用Java核心库实现解压缩文件功能,读者也可以使用第三方库Zip4j尝试更多功能。