개발일지 - 압축풀기

아침7시개발·2022년 4월 6일
0

개발일지

목록 보기
11/19

사내 개발에서 e-book을 업로드 할 수 있게 요청이 왔다.
e-book은 html,css,js 파일들이 압축되어 있는 zip파일이다.


팀장님이 누가 할지 물었고, 마침 일이 거의 끝나가 내가 하겠다고 했다.
지금까지 업로드는 계속 해왔으니 이번에도 별로 다를것 없이 복붙으로 코딩 해넣고 마무리로 조금만 손보면 될 줄 알았다.

바로 코딩에 들어갔다. 업로드는 이미 있는 코드를 사용했다.

private static Map<String,String> saveFilesNow( String classPath, Integer seq, MultipartFile files, boolean isTmp) {
        Map<String,String>result = new HashMap<String,String>();
        try {
            logger.debug("file upload start!!!!!!!!");
            String rootPath = null;
            if (!isTmp) {
                rootPath = Constant.UPLOAD_DIR + Constant.DIR_SEPARATOR + classPath;
                //uploadDir 생성
                if (!new File(Constant.UPLOAD_DIR).exists()) {
                    try {
                        logger.debug("uploadDir: "+Constant.UPLOAD_DIR);
                        new File(Constant.UPLOAD_DIR).mkdir();
                    } catch (Exception e) {
                        logger.error("oops!", e);
                    }
                }
            } else {
                rootPath = Constant.TMP_DIR + Constant.DIR_SEPARATOR + classPath;
                // tmpDir 생성
                if (!new File(Constant.TMP_DIR).exists()) {
                    try {
                        logger.debug("tmpDir: "+Constant.TMP_DIR);
                        new File(Constant.TMP_DIR).mkdir();
                    } catch (Exception e) {
                        logger.error("oops!", e);
                    }
                }
            }

            if(!files.getOriginalFilename().equals("")) {
                String origFilename = files.getOriginalFilename();
                /* 실행되는 위치의 'files' 폴더에 파일이 저장됩니다. */
                String savePath = rootPath + Constant.DIR_SEPARATOR + seq;
                /* 파일이 저장되는 폴더가 없으면 폴더를 생성합니다. */
                String dbSavePath = classPath + Constant.DIR_SEPARATOR + seq;
                String filePath = savePath + Constant.DIR_SEPARATOR + origFilename;

                if (!new File(rootPath).exists()) {
                    try {
                        logger.debug("rootPath: "+rootPath);
                        new File(rootPath).mkdir();
                    } catch (Exception e) {
                        logger.error("oops!", e);
                    }
                }
                if (!new File(savePath).exists()) {
                    try {
                        logger.debug("savePath: "+savePath);
                        new File(savePath).mkdir();
                    } catch (Exception e) {
                        logger.error("oops!", e);
                    }
                }
                // [주의] Don't use transferTo() method. NOT working! files.transferTo(new File(filePath));
                InputStream fileStream = files.getInputStream();

                FileUtils.copyInputStreamToFile(fileStream, new File(filePath));    //파일 저장
                result.put("savePath",dbSavePath);
                result.put("origFilename",origFilename);
                result.put("filePath",savePath);
            }
        } catch (IOException e) {
            logger.error("oops!", e);
        }

        return result;
    }
    

이런식으로 구성을 해서 파일이 업로드 되는 것을 확인했다.
이제 구글링으로 압축해제 하는 것만 찾아서 넣기만 하면 된다.
바로 눈에 보인 java util을 사용하는 method를 적용해 보기로 했다.

/**
  * 압축 해제
  * @param filePath
  * @param tempPath
  * @param fileName
  * @return
  * @throws IOException
  */
 public static Integer decompress(String filePath, String tempPath , String fileName) throws IOException {
     ZipInputStream zis = null;
     Charset CP866 = Charset.forName("CP866");
     // e-book/bookseq/filename
     String fileZip;
     File destDir = null;
     try {
         fileZip = filePath + Constant.DIR_SEPARATOR + fileName;

         // destDir = new File(filePath);//폴더
         destDir = new File(tempPath);//템프 폴더
         byte[] buffer = new byte[1024];
         zis = new ZipInputStream(new FileInputStream(fileZip),CP866);//압축파일
         ZipEntry zipEntry = zis.getNextEntry();
         int read = zis.read();

         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();
         }
     } catch (IOException e) {
         logger.error(e);
     }finally{
         zis.closeEntry();
         zis.close();
     }
     return destDir.list().length;
 }

 /**
  * 폴더, 파일 생성
  * @param destinationDir
  * @param zipEntry
  * @return
  * @throws IOException
  */
 public static File newFile(File destinationDir, ZipEntry zipEntry) throws IOException {
     File destFile = null;
     try {
         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());
         }
     } catch (IOException e) {
         logger.error(e);
     }

     return destFile;
 }
 
     /**
  * 폴더, 파일 복사
  * @param sourceF
  * @param targetF
  * @param flag
  */
 public static void copy(File sourceF, File targetF, boolean flag){

     File[] target_file = sourceF.listFiles();
     for (File file : target_file) {
         File temp = new File(targetF.getAbsolutePath() + File.separator + file.getName());
         if(file.isDirectory()){
             temp.mkdir();
             copy(file, temp, false);
         } else {
             FileInputStream fis = null;
             FileOutputStream fos = null;
             try {
                 fis = new FileInputStream(file);
                 fos = new FileOutputStream(temp) ;
                 byte[] b = new byte[4096];
                 int cnt = 0;
                 while((cnt=fis.read(b)) != -1){
                     fos.write(b, 0, cnt);
                 }
             } catch (Exception e) {
                 e.printStackTrace();
             } finally{
                 try {
                     fis.close();
                     fos.close();
                 } catch (IOException e) {
                     // TODO Auto-generated catch block
                     logger.error(e);
                 }

             }
         }
     }
 }

폴더에 압축을 해제하고 최상위 폴더가 없으면 최상위 폴더를 만들어서 그 폴더에 압축을 해제해달라는 추가 요청이 들어왔다.

여러가지 시행착오가 있었지만 처음에 같은 폴더에서 압축해제하고 이동을 하니 무한루프를 타고 서버 용량을 초과해버렸다.

temp파일을 만들어서 temp파일에 압축을 해제한 뒤에 옮기는 방식으로 변경했다.
이렇게 git에 request 올리고 서버에서 다시 실행한 결과 한글이 깨지는 에러가 발생했다.

한글은 언제나 말썽이기 때문에 가능하면 사용하지 않는게 좋다.

java util은 버그가 있어서 한글이 깨지는 모양이다.
구글링을 해보니 3가지 이상의 방법이 있는듯 하다.

  1. jazzlib 사용
  2. commons-compress 사용
  3. org.apache.tools.zip.ZipFile 사용해서 charset 설정하기

라이브러리를 추가하는 것은 팀장님과 상의를 해봤다.

일단 1번 jazzlib은 gradle 지원이 아니고 외부 라이브러리를 추가해야 하기 때문에 제외했다.
그래서, 2번 commons-compress를 사용해 봤는데 한글이 여전히 깨진다.

마지막으로 3번을 해보기 전에 zip4j를 사용하고 있으니 사용하던 라이브러리를 사용해서 압축해제를 해보기로 했다.


 /**
  * 파일 압축 해제
  * @param file
  * @param target
  */
 public static void unzip(File file, String target)  {
     try {
         ZipFile zipFile = new ZipFile(file);
         UnzipParameters param = new UnzipParameters();
         String EUCKR = "EUC-KR";
         zipFile.setCharset(Charset.forName(EUCKR));
         List list = zipFile.getFileHeaders();
         list.forEach(i -> {
             FileHeader fh = (FileHeader) i;

             byte[] b = new byte[0];
             try {
                 b = fh.getFileName().getBytes(EUCKR);
             } catch (UnsupportedEncodingException e) {
                 logger.error(e);
             }
             String fname = null;
             try {
                 CharBuffer cbuffer = CharBuffer.wrap((new String( b, EUCKR)).toCharArray());
                 Charset utf8charset = StandardCharsets.UTF_8;
                 ByteBuffer bbuffer = utf8charset.encode(cbuffer);
                 fname = new String(bbuffer.array());
                 System.out.println("fname = " + fname);
                 //fname = new String( b, StandardCharsets.ISO_8859_1);
//                    if (fname.getBytes(EUCKR).length != b.length) {
//                        fname = new String(b, "EUC-KR");//most possible charset
//                    }
             } catch (Exception e) {
                 //try other charset or ...
                 logger.error("Invalid file name: " + fname);
             }

             try {
                 zipFile.extractFile( fh, target, fname, param );
             } catch (ZipException ex) {
                 logger.error(ex);
             }
         });

     } catch (IllegalArgumentException e) {
         logger.error(e);
     } catch (ZipException e) {
         logger.error(e);
     }
 }

압축해제하는 방식을 구글링해서 복붙한 뒤에 입맛에 맞게 변경해 봤다.
현재 사용하고 있는 버전이 2.9.0인데 아무래도 버그가 있는듯 하다.
2일간 에러가 나는 곳을 살펴 봤는데 에러가 날 곳이 아닌 곳에서 에러가 난다.
다시 2.9.X에 대해 구글링해보니 버그가 있어서 2.10.0으로 fix됐다는 것을 알았다.

그래서, 버전업을 하니 에러가 나오지 않았다.
이제 윈도우에서 에러는 나지 않았다. 바로 서버에 올려서 확인 해보자.

뭐지?

 /**
     * 파일 압축 해제
     * @param file
     * @param target
     */
    public static void unzip(File file, String target)  {
        try {
            ZipFile zipFile = new ZipFile(file);
            UnzipParameters param = new UnzipParameters();
            String encode = "cp949";
            zipFile.setCharset(Charset.forName(encode));
            List list = zipFile.getFileHeaders();
            list.forEach(i -> {
                FileHeader fh = (FileHeader) i;

                byte[] b = new byte[0];
                try {
                    b = fh.getFileName().getBytes(encode);
                } catch (UnsupportedEncodingException e) {
                    logger.error(e);
                }
                String fname = null;
                try {
                    Charset encodeCharset = Charset.forName(encode);
                    CharBuffer charBuffer = encodeCharset.decode(ByteBuffer.wrap(b));
                    fname = charBuffer.toString();
                } catch (Exception e) {
                    //try other charset or ...
                    logger.error("Invalid file name: " + fname);
                }

                try {
                    zipFile.extractFile( fh, target, fname, param );
                } catch (ZipException ex) {
                    logger.error(ex);
                }
            });

        } catch (IllegalArgumentException e) {
            logger.error(e);
        } catch (ZipException e) {
            logger.error(e);
        }
    }

에러가 나는게 잘 생각해보니 linux환경은 utf-8인데 zip파일은 cp949이다.
그러므로 cp949로 변경해서 byte로 받고 다시 utf-8로 변경해서 파일이름을 저장하면 될것 같았다.
바로 이렇게 바꿔서 해결했다.

profile
쉬엄쉬엄하는 개발자

0개의 댓글