[Spring MVC & Java] zip 압축 및 다운로드

식빵·2021년 12월 20일
1

Spring Lab

목록 보기
2/35
post-thumbnail

🍀 개요

하나의 게시물에 있는 다수의 파일들을 하나의 zip 압축하여 다운로드 받는 기능을 구현했다.

ZipOutputStream, ZipEntry 을 통하여 구현했다.



🍀 테스트 파일 및 경로

아래와 같이 파일들을 준비한다.

경로: D:\zip_test_folder\ 

파일 목록:
four.png
one.PNG
three.PNG
two.PNG

🍀 java 코드

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class FileZipModule {
    private final Logger logger = LoggerFactory.getLogger(FileZipModule.class);

    public void zip() {
        String where = "D:/zip_test_folder";
        File file_ = new File(where);
        File[] listFiles = file_.listFiles();

        FileOutputStream fos = null;
        ZipOutputStream zipOut = null;
        FileInputStream fis = null;
        try {

            fos = new FileOutputStream("D:/zip_test_folder/wow.zip");
            zipOut = new ZipOutputStream(fos);

            for(File fileToZip :  listFiles) {

                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.closeEntry();

            }

            zipOut.close();
            fos.close();

        } catch (IOException e) {
            logger.error("Error While Zipping {}", e.getMessage());
        } finally {
            if (fis != null) {
                try {fis.close();} catch (IOException e) {logger.debug("ignoring IO Exception While FileInputStream close, {}", e.getMessage());}
            }

            if(zipOut != null) {
                try {zipOut.closeEntry();} catch (IOException e) {logger.debug("ignoring IO Exception while ZipOutputStream -> CloseEntry, {}", e.getMessage());}
            }

            if (zipOut != null) {
                try {zipOut.close();} catch (IOException e) {logger.debug("ignoring IO Exception while ZipOutputStream -> close, {}", e.getMessage());}
            }

            if (fos != null) {
                try {fos.close();} catch (IOException e) {logger.debug("ignoring IO Exception While FileOutputStream close, {}", e.getMessage());}
            }
        }
    }

    /**
     * 테스트 코드
     */
    public static void main(String[] args) {
        new FileZipModule().zip();
    }

}



결과

압축파일 위치로 가면 zip 파일을 확인할 수 있다.
그리고 zip 파일에 어떤 파일이 있는 지 보면 정상적으로 zip이 생성된 것을 알 수 있다.





🍀 Spring Mvc 에서는 어떻게...?

위에서는 그냥 로컬에서 돌린 코드고, 내가 필요한 건 HttpServletResponse에
태워서 client가 해당 파일을 다운 받도록 해야 한다.

이를 위해서 아래와 같이 코드를 조금만 응용해봤다.

@RequestMapping(value = "/downloadZipFile.do")
public void downloadZipFile(@RequestParam("bbsId") String bbsId, @RequestParam("atchmnflId") String atchmnflId, HttpServletResponse response) {

    response.setStatus(HttpServletResponse.SC_OK);
    response.setContentType("application/zip");
    response.addHeader("Content-Disposition", "attachment; filename=\"allToOne.zip\"");

    FileOutputStream fos = null;
    ZipOutputStream zipOut = null;
    FileInputStream fis = null;

    try {
        zipOut = new ZipOutputStream(response.getOutputStream());

	// DB에 저장되어 있는 파일 목록을 읽어온다.
        List<CmmnNttAtflDtlVO> atchmnFileInfoList = bbsService.atchmnFlList(atchmnflId);

	// 실제 Server에 파일들이 저장된 directory 경로를 구해온다.
        String filePath = BbsInfoFinder.mapFileLoadPath(bbsId);

	// File 객체를 생성하여 List에 담는다.
        List<File> fileList = atchmnFileInfoList.stream().map(fileInfo -> {
            return new File(filePath + "/" + fileInfo.getStreFileNm());
        }).collect(Collectors.toList());

	// 루프를 돌며 ZipOutputStream에 파일들을 계속 주입해준다.
        for(File file : fileList) {
            zipOut.putNextEntry(new ZipEntry(file.getName()));
            fis = new FileInputStream(file);

            StreamUtils.copy(fis, zipOut);

            fis.close();
            zipOut.closeEntry();
        }

        zipOut.close();

    } catch (IOException e) {
        System.out.println(e.getMessage());
        try { if(fis != null)fis.close(); } catch (IOException e1) {System.out.println(e1.getMessage());/*ignore*/}
        try { if(zipOut != null)zipOut.closeEntry();} catch (IOException e2) {System.out.println(e2.getMessage());/*ignore*/}
        try { if(zipOut != null)zipOut.close();} catch (IOException e3) {System.out.println(e3.getMessage());/*ignore*/}
        try { if(fos != null)fos.close(); } catch (IOException e4) {System.out.println(e4.getMessage());/*ignore*/}
    }
}




🍀 좀 더 쉬운 방법은 없을까?

https://github.com/srikanth-lingala/zip4j 를 써보는 건 어떨까?

maven 을 사용한다면 pom.xml 에 dependency 를 추가한다.

<dependency>
    <groupId>net.lingala.zip4j</groupId>
    <artifactId>zip4j</artifactId>
    <version>2.11.5</version>
</dependency>

그리고 사용법은... git repository 에서 스스로 익혀보길 바란다.

상당히 사용법을 잘 적어줬기 때문에 스스로 익히는데 어려움은 없을 거라고 생각한다.
추후에 이 라이브러리와 관련된 글을 따로 작성해보겠다.





🍀 참고

https://www.baeldung.com/java-compress-and-uncompress
https://stackoverflow.com/questions/27952949/spring-rest-create-zip-file-and-send-it-to-the-client

디렉토리 구조도 유지하면서 zip 하기:
https://stackoverflow.com/questions/1399126/java-util-zip-recreating-directory-structure

profile
백엔드를 계속 배우고 있는 개발자입니다 😊

3개의 댓글

comment-user-thumbnail
2022년 10월 18일

마침 찾던기술이에요 ㅠㅠ 감사합니다

답글 달기
comment-user-thumbnail
2023년 4월 23일

혹시.. 따로 에러는 없는데 아무런 동작이 일어나지 않는 이유를 아실까요.. for문안에 파일명과 경로는 정상적으로 출력됩니다.

1개의 답글