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.txttest2.txtmultiCompressed.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. 디렉토리 압축

이제 전체 디렉토리를 압축하는 방법에 대해 논의해 보겠습니다. zipTestdirCompressed.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 에서 찾을 수 있습니다 .

Junit footer banner