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 mjs mjs 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 mjs mjs 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 - hashCode(), 사용하는 이유? 구현 방법?
- Java8의 Stream reduce() 사용 방법 및 예제
- Java - filter, map, flatMap 사용 방법 및 예제
- Java - 2개의 Map 합치기 (merge, putAll)
- Java - ConcurrentModificationException 원인 및 해결 방법
- JUnit - @After와 @AfterClass의 차이점
- JUnit - @Before와 @BeforeClass의 차이점
- java와 javac의 차이점
- Java - 자바(JDK) 버전 확인 방법 (터미널, cmd 명령어)
- Java - java.util.Date를 java.sql.Date로 변환
- Java - 시스템 운영체제(OS) 정보 확인
- Java - 코드 실행 시간 측정
- Java - HashSet.retainAll() 사용 방법 및 예제
- Java - ArrayList.retainAll() 사용 방법 및 예제
- Java - ArrayList를 String으로 변환
- Java - float을 int로 변환
- Java - float을 String으로 변환
- Java - String을 boolean으로 변환
- Java - XML을 JSON으로 변환
- Java - ClassNotFoundException 발생 원인 및 해결 방법
- Java - private 생성자를 사용하는 이유
- Java - non-static method cannot be referenced from a static context
- Java - NoSuchMethodError 원인 및 해결 방법
- Java - Object를 byte[]로 변환
- Java - AbstractMethodError 원인 및 해결
- NoClassDefFoundError: com/fasterxml/jackson/databind/ObjectMapper 에러
- Java - HttpClient에 Timeout 적용
- IntelliJ에서 Java 실행 파일 배포 (Export Runnable JAR)
- Java - JAR 디컴파일 방법 (JD-GUI, JD-CLI)
- Java - 키보드, 마우스 이벤트 받기 (이벤트 후킹)
- Java에서 윈도우 cmd 명령어 실행 및 결과 출력
- Java - Selenium 드라이버 자동 설치 방법
- Java - JSON 라이브러리 사용 방법 (JSONObject, JSONArray)
- Java - ZIP 압축, 압축 해제 (zip, unzip)
- Java - byte[] 배열을 File에 저장