[JAVA] 자바에서 만든 zip 파일이 맥(Mac)에서 내부 파일명 한글깨짐/한글 깨지는 경우

[JAVA] 자바에서 만든 zip 파일이 맥(Mac)에서 내부 파일명 한글깨짐/한글 깨지는 경우
자바에서 만든 zip 파일을 맥(Mac)에서 압축해제 시 내부 파일명 한글깨짐/한글 깨지는 경우다.
zip 파일명 자체는 깨지지 않는데, zip 내부 파일들의 파일명이 깨지는 경우다.
처음에는 zip 파일 다운로드하는 과정에 문제가 있다고 생각했는데, zip 압축파일을 만들어내는 방식의 문제였다.

이것은 zip 파일을 압축할 때 파일명을 유니코드(UTF-8)로 저장하는 방식을 사용해서 해결할 수 있다.

그런데 윈도우의 몇몇 압축 프로그램의 경우에는 오히려 파일명을 유니코드(UTF-8)로 해서 압축했을 때 이름이 깨지는 경우도 있다고 하니까, 맥 OS를 사용하는 경우에 대해서만 처리를 해주는 것을 권장한다.

방법은 자바에서 zip 압축파일을 만들 때 ZipOutputStream 객체(import org.apache.tools.zip.ZipOutputStream;) 가 아닌, ZipArchiveOutputStream 객체(import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;) 를 사용하면 된다.

기존 코드에서 ZipOutputStream 객체를 사용하고 있다면 해당 객체만 ZipArchiveOutputStream 객체로 바꿔서 테스트해보자.

ZipArchiveOutputStream 객체를 사용하기 위해서는 Apache Common Compress 라이브러리를 임포트하면 된다.

http://commons.apache.org/compress/ 에서 구할 수 있고, https://mvnrepository.com/artifact/org.apache.commons/commons-compress/1.20 에서도 곧바로 다운로드받을 수 있다.

필자는 1.20 버전을 사용했다. (commons-compress-1.20.jar)

이때 자바 압축 코드의 핵심은 파일명을 UTF-8 인코딩 설정하는 아래 코드이다.

    zipOutputStream = new ZipArchiveOutputStream(fileOutputStream);

    // Mac OS 사용하는 경우 zip 압축파일 내부 파일명 깨지는 문제 방지
    zipOutputStream.setEncoding(“UTF-8”);
    zipOutputStream.setFallbackToUTF8(true);
    zipOutputStream.setUseLanguageEncodingFlag(true);
    zipOutputStream.setCreateUnicodeExtraFields(ZipArchiveOutputStream.UnicodeExtraFieldPolicy.ALWAYS);

전체적인 코드 수정은 아래와 같이 하면 된다.

[AS-IS]

    public static void makeZip() {
        
        File outputFile = null;
        FileOutputStream fileOutputStream = null;
        ZipOutputStream zipOutputStream = null;
        
        try {
            outputFile = new File(“C:\\test\\output.zip”);
            fileOutputStream = new FileOutputStream(outputFile);
            
            zipOutputStream = new ZipOutputStream(fileOutputStream);
            zipOutputStream.setEncoding(“KSC5601”);
    
            ZipEntry zipEntry = new ZipEntry(“input.txt”);
            zipOutputStream.putNextEntry(zipEntry);
    
            FileInputStream fileInputStream = null;
            try {
                byte[] buf = new byte[4096];
                int byteRead = 0;
                
                File inputFile = new File(“C:\\test\\테스트.txt”);
                fileInputStream = new FileInputStream(inputFile);
        
                while ((byteRead = fileInputStream.read(buf)) > 0) {
                 zipOutputStream.write(buf, 0, byteRead);
                }
                
                zipOutputStream.flush();
                zipOutputStream.closeEntry();
                
            } catch (Exception e) {
                throw e;
                
            } finally {
                try {
                    if (fileInputStream != null) {
                        fileInputStream.close();
                    }
                } catch (Exception e) {}
            }
            
        } catch (Exception e) {
            e.printStackTrace();
            
        } finally {
            try {
                if (zipOutputStream != null) {
                    zipOutputStream.close();
                }
            } catch (Exception e) {}
            
            try {
                if (fileOutputStream != null) {
                    fileOutputStream.close();
                }
            } catch (Exception e) {}
        }
    }

[TO-BE]

    public static void makeZip() {
        
        File outputFile = null;
        FileOutputStream fileOutputStream = null;
        ZipArchiveOutputStream zipOutputStream = null;
        
        try {
            outputFile = new File(“C:\\test\\output.zip”);
            fileOutputStream = new FileOutputStream(outputFile);
            
            zipOutputStream = new ZipArchiveOutputStream(fileOutputStream);


            // Mac OS 사용하는 경우 zip 압축파일 내부 파일명 깨지는 문제 방지
            zipOutputStream.setEncoding(“UTF-8”);
            zipOutputStream.setFallbackToUTF8(true);
            zipOutputStream.setUseLanguageEncodingFlag(true);
            zipOutputStream.setCreateUnicodeExtraFields(ZipArchiveOutputStream.UnicodeExtraFieldPolicy.ALWAYS);
    
            ZipArchiveEntry zipEntry = new ZipArchiveEntry(“input.txt”);
            zipOutputStream.putArchiveEntry(zipEntry);
            
            FileInputStream fileInputStream = null;
            try {
                byte[] buf = new byte[4096];
                int byteRead = 0;
                
                File inputFile = new File(“C:\\test\\테스트.txt”);
                fileInputStream = new FileInputStream(inputFile);
        
                while ((byteRead = fileInputStream.read(buf)) > 0) {
                 zipOutputStream.write(buf, 0, byteRead);
                }
                
                zipOutputStream.flush();
                zipOutputStream.closeArchiveEntry();
                
            } catch (Exception e) {
                throw e;
                
            } finally {
                try {
                    if (fileInputStream != null) {
                        fileInputStream.close();
                    }
                } catch (Exception e) {}
            }
            
        } catch (Exception e) {
            e.printStackTrace();
            
        } finally {
            try {
                if (zipOutputStream != null) {
                    zipOutputStream.close();
                }
            } catch (Exception e) {}
            
            try {
                if (fileOutputStream != null) {
                    fileOutputStream.close();
                }
            } catch (Exception e) {}
        }
    }

참고사이트 1 : https://visu4l.tistory.com/394

참고사이트 3 : https://huskdoll.tistory.com/m/189?category=526538