Java에서 Zip 파일을 압축하거나 압축 해제하는 방법을 소개합니다.
- 파일 압축
- 디렉토리 압축
- 파일, 디렉토리 압축 (zip4j 라이브러리)
- 압축 해제
- 압축 해제 (zip4j 라이브러리)
1. 파일 압축
다음은 폴더가 아닌, 파일들을 압축하는 예제입니다.
- Zip 압축을 할 때는
ZipOutputStream
를 사용하여 파일을 저장합니다. - 다수의 파일을 압축할 때, 파일을 구분하기 위해 ZipEntry 객체를 만듭니다.
ZipEntry.putNextEntry()
호출 후에 파일을 write하며, 모두 write하면ZipEntry.closeEntry()
를 호출합니다.srcPaths
배열에 입력된 파일을 압축하기 위해 반복문으로 다수의 ZipEntry를 write하도록 구현하였습니다.
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipExample {
public static void main(String[] args) throws IOException {
String[] srcPaths = {
"/var/tmp/sample.txt",
"/var/tmp/sample1.txt",
"/var/tmp/sample2.txt"
};
zipFiles(srcPaths, "my_files.zip");
}
public static void zipFiles(String[] srcPaths, String zipFileName) {
try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFileName))) {
for (String srcPath : srcPaths) {
Path src = Paths.get(srcPath);
try (FileInputStream fis = new FileInputStream(src.toFile())) {
ZipEntry zipEntry = new ZipEntry(src.getFileName().toString());
zos.putNextEntry(zipEntry);
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) > 0) {
zos.write(buffer, 0, len);
}
zos.closeEntry();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
위 프로그램 실행 시, 프로젝트의 Root 디렉토리를 보면 Zip 파일이 생성되어있습니다.
$ ls -al my_files.zip
-rw-rw-r-- 1 js js 3993 10 13 21:22 my_files.zip
2. 디렉토리 압축
이번에는 폴더와 그 하위 파일들을 모두 압축하려고 합니다.
예제에서 아래의 디렉토리를 압축할 것입니다.
/var/tmp/uml$ tree
.
├── notify_app_target_event
│ ├── sequence
│ │ └── sequence.png
│ └── sequence.uml
└── request_prediction_update
├── sequence
│ └── sequence.png
└── sequence.uml
4 directories, 4 files
아래 예제는 다음 예제는 Files.walkFileTree()
으로 디렉토리의 하위 폴더, 파일들을 모두 순회하면서 압축합니다. 파일들이 많기 때문에 각각의 파일은 ZipEntry로 구분해야합니다.
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class ZipExample2 {
public static void main(String[] args) {
Path srcDir = Paths.get("/var/tmp/uml");
zipFiles(srcDir, "uml.zip");
}
public static void zipFiles(Path srcDir, String zipFileName) {
try (ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(zipFileName))) {
Files.walkFileTree(srcDir, new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attributes) {
// only copy files, no symbolic links
if (attributes.isSymbolicLink()) {
return FileVisitResult.CONTINUE;
}
try (FileInputStream fis = new FileInputStream(file.toFile())) {
Path targetFile = srcDir.relativize(file);
zos.putNextEntry(new ZipEntry(targetFile.toString()));
byte[] buffer = new byte[1024];
int len;
while ((len = fis.read(buffer)) > 0) {
zos.write(buffer, 0, len);
}
zos.closeEntry();
} catch (IOException e) {
e.printStackTrace();
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) {
System.err.printf("Unable to zip : %s%n%s%n", file, exc);
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
위의 코드를 실행하면, 프로젝트 Root에 zip 파일이 생성됩니다.
$ ls -al uml.zip
-rw-rw-r-- 1 js js 41436 10월 13 22:18 uml.zip
3. 파일, 디렉토리 압축 (zip4j)
zip4j 라이브러리를 이용하면 간단히 Zip 파일로 압축할 수 있습니다.
zip4j 라이브러리는 Gradle 프로젝트에서 build.gradle
에 다음과 같이 의존성을 추가하시면 됩니다.
dependencies {
compile group: 'net.lingala.zip4j', name: 'zip4j', version: '2.6.1'
...
}
3.1 파일 1개 압축
다음과 같이 압축할 source 파일 경로 및 Target 파일 경로를 입력하시면 됩니다.
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
public class ZipExample3 {
public static void main(String[] args) throws ZipException {
ZipFile zipFile = new ZipFile("filename.zip");
zipFile.addFile("file.txt");
}
}
3.2 다수 파일 압축
다수 파일의 경로는 리스트로 만들어 인자로 전달합니다.
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import java.io.File;
import java.util.Arrays;
import java.util.List;
public class ZipExample3 {
public static void main(String[] args) throws ZipException {
List<File> files = Arrays.asList(
new File("file1.txt"), new File("file2.txt"));
ZipFile zipFile = new ZipFile("filename.zip");
zipFile.addFiles(files);
}
}
3.3 디렉토리 압축
디렉토리 경로를 addFolder()
인자로 전달하여 압축합니다.
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import java.io.File;
public class ZipExample3 {
public static void main(String[] args) throws ZipException {
ZipFile zipFile = new ZipFile("filename.zip");
zipFile.addFolder(new File("/home/mkyong/folder"));
}
}
4. 압축 해제
다음은 Zip 파일을 압축 해제하는 예제입니다.
- 압축 해제는
ZipInputStream
를 이용합니다. - 반복문을 이용하여 ZipEntry 형태로 내부 파일들을 순회합니다.
- ZipEntry의 파일의 경로를 계산하고, 동일한 상대 경로에 파일을 복사합니다.
Files.copy()
로 Zip 파일을 복사합니다.zipSlipProtect()
에는 Zip의 보안 취약점을 체크하는 코드들이 있습니다.
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
public class UnzipExample {
public static void main(String[] args) {
Path source = Paths.get("/var/tmp/uml.zip");
Path target = Paths.get("/var/tmp/");
unzipFile(source, target);
}
public static void unzipFile(Path sourceZip, Path targetDir) {
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(sourceZip.toFile()))) {
// list files in zip
ZipEntry zipEntry = zis.getNextEntry();
while (zipEntry != null) {
boolean isDirectory = false;
if (zipEntry.getName().endsWith(File.separator)) {
isDirectory = true;
}
Path newPath = zipSlipProtect(zipEntry, targetDir);
if (isDirectory) {
Files.createDirectories(newPath);
} else {
if (newPath.getParent() != null) {
if (Files.notExists(newPath.getParent())) {
Files.createDirectories(newPath.getParent());
}
}
// copy files
Files.copy(zis, newPath, StandardCopyOption.REPLACE_EXISTING);
}
zipEntry = zis.getNextEntry();
}
zis.closeEntry();
} catch (IOException e) {
e.printStackTrace();
}
}
public static Path zipSlipProtect(ZipEntry zipEntry, Path targetDir)
throws IOException {
// test zip slip vulnerability
Path targetDirResolved = targetDir.resolve(zipEntry.getName());
// make sure normalized file still has targetDir as its prefix
// else throws exception
Path normalizePath = targetDirResolved.normalize();
if (!normalizePath.startsWith(targetDir)) {
throw new IOException("Bad zip entry: " + zipEntry.getName());
}
return normalizePath;
}
}
Output:
/var/tmp/uml$ tree
.
├── notify_app_target_event
│ ├── sequence
│ │ └── sequence.png
│ └── sequence.uml
└── request_prediction_update
├── sequence
│ └── sequence.png
└── sequence.uml
5. 압축 해제 (zip4j)
zip4j 라이브러리를 이용하면 쉽게 압축을 해제할 수 있습니다. 아래와 같이 source와 target을 인자로 전달하면 됩니다.
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import java.io.File;
public class UnzipExample2 {
public static void main(String[] args) throws ZipException {
File source = new File("/var/tmp/uml.zip");
String target = "/var/tmp/";
ZipFile zipFile = new ZipFile(source);
zipFile.extractAll(target);
}
}
References
Loading script...
Related Posts
- Java - Unsupported class file major version 61 에러
- Java - String.matches()로 문자열 패턴 확인 및 다양한 예제 소개
- Java - 문자열 공백제거 (trim, replace)
- Java - replace()와 replaceAll()의 차이점
- Java - ArrayList 초기화, 4가지 방법
- Java - 배열 정렬(Sorting) (오름차순, 내림차순)
- Java - 문자열(String)을 비교하는 방법 (==, equals, compare)
- Java - StringBuilder 사용 방법, 예제
- Java - 로그 출력, 파일 저장 방법 (Logger 라이브러리)
- Java IllegalArgumentException 의미, 발생 이유
- Java - NullPointerException 원인, 해결 방법
- Seleninum의 ConnectionFailedException: Unable to establish websocket connection 해결
- Java - compareTo(), 객체 크기 비교
- Java - BufferedWriter로 파일 쓰기
- Java - BufferedReader로 파일 읽기
- Java charAt() 함수 알아보기
- Java - BigInteger 범위, 비교, 연산, 형변환
- Java contains()로 문자(대소문자 X) 포함 확인
- Java - Set(HashSet)를 배열로 변환
- Java - 문자열 첫번째 문자, 마지막 문자 확인
- Java - 문자열 한글자씩 자르기
- Java - 문자열 단어 개수 가져오기
- Java - 1초마다 반복 실행
- Java - 배열을 Set(HashSet)로 변환
- Java - 여러 Set(HashSet) 합치기
- Java - 명령행 인자 입력 받기
- Java - 리스트 역순으로 순회, 3가지 방법
- Java - 특정 조건으로 리스트 필터링, 3가지 방법
- Java - HashMap 모든 요소들의 합계, 평균 계산
- Java - 특정 조건으로 HashMap 필터링
- Java - 싱글톤(Singleton) 패턴 구현
- Java - 숫자 왼쪽에 0으로 채우기
- Java - String 배열 초기화 방법
- Java - 정렬된 순서로 Map(HashMap) 순회
- Java - HashMap에서 key, value 가져오기