1. 개요
이 빠른 사용방법(예제)에서는 Java에서 제공하는 핵심 라이브러리를 사용하여 파일을 아카이브로 압축하는 방법과 아카이브의 압축을 푸는 방법에 대해 설명합니다.
이러한 핵심 라이브러리는 모든 압축 및 압축 해제 관련 유틸리티를 찾을 수 있는 java.util.zip 패키지의 일부입니다 .
2. 파일 압축
먼저 단일 파일을 압축하는 간단한 작업을 살펴보겠습니다.
여기 예제에서는 test1.txt 라는 파일을 압축 된 압축 된 .zip이라는 이름 의 아카이브에 압축 합니다.
물론 먼저 디스크에서 파일에 액세스합니다. 살펴보겠습니다.
public class ZipFile {
public static void main(String[] args) throws IOException {
String sourceFile = "test1.txt";
FileOutputStream fos = new FileOutputStream("compressed.zip");
ZipOutputStream zipOut = new ZipOutputStream(fos);
File fileToZip = new File(sourceFile);
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);
}
zipOut.close();
fis.close();
fos.close();
}
}
3. 여러 파일 압축
다음으로 여러 파일을 하나의 zip 파일로 압축하는 방법을 살펴보겠습니다. test1.txt 및 test2.txt 를 multiCompressed.zip 으로 압축합니다 .
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();
}
}
4. 디렉토리 압축
이제 전체 디렉토리를 압축하는 방법에 대해 논의해 보겠습니다. zipTest 를 dirCompressed.zip에 디렉터리 로 지정합니다 .
public class ZipDirectory {
public static void main(String[] args) throws IOException {
String sourceFile = "zipTest";
FileOutputStream fos = new FileOutputStream("dirCompressed.zip");
ZipOutputStream zipOut = new ZipOutputStream(fos);
File fileToZip = new File(sourceFile);
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("/")) {
zipOut.putNextEntry(new ZipEntry(fileName));
zipOut.closeEntry();
} else {
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();
}
}
참고:
- 하위 디렉토리를 압축하기 위해 재귀적으로 반복합니다.
- 디렉토리를 찾을 때마다 해당 이름을 하위 ZipEntry 이름에 추가 하여 계층 구조를 저장합니다.
- 우리는 또한 모든 빈 디렉토리에 대한 디렉토리 항목을 생성합니다.
5. 아카이브 압축 풀기
이제 아카이브의 압축을 풀고 내용을 추출해 보겠습니다.
이 예에서, 우리는 unzipTest 라는 새로운 폴더에 압축 을 풀 것 입니다.
살펴보겠습니다.
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) {
// ...
}
zis.closeEntry();
zis.close();
}
}
while 루프 내에서 각 ZipEntry를 반복 하고 먼저 디렉토리인지 확인합니다 . 그렇다면 mkdirs() 메소드를 사용하여 디렉토리를 생성합니다 . 그렇지 않으면 파일 생성을 계속합니다.
while (zipEntry != null) {
File newFile = newFile(destDir, zipEntry);
if (zipEntry.isDirectory()) {
if (!newFile.isDirectory() && !newFile.mkdirs()) {
throw new IOException("Failed to create directory " + newFile);
}
} else {
// fix for Windows-created archives
File parent = newFile.getParentFile();
if (!parent.isDirectory() && !parent.mkdirs()) {
throw new IOException("Failed to create directory " + parent);
}
// write file content
FileOutputStream fos = new FileOutputStream(newFile);
int len;
while ((len = zis.read(buffer)) > 0) {
fos.write(buffer, 0, len);
}
fos.close();
}
zipEntry = zis.getNextEntry();
}
여기서 한 가지 주의할 점은 else 분기에서 파일의 상위 디렉토리가 존재하는지 먼저 확인한다는 것입니다. 이는 루트 디렉토리가 zip 파일에 해당 항목이 없는 Windows에서 생성된 아카이브에 필요합니다.
newFile() 메서드 에서 또 다른 핵심 사항을 볼 수 있습니다 .
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;
}
이 방법은 대상 폴더 외부의 파일 시스템에 파일을 쓰는 것을 방지합니다. 이 취약점을 Zip Slip이라고 하며 여기에서 자세히 읽을 수 있습니다 .
6. 결론
이 예제에서는 파일 압축 및 압축 해제 작업을 위해 Java 라이브러리를 사용하는 방법을 설명했습니다.
이 예제의 구현은 GitHub 에서 찾을 수 있습니다 .