개발 메모장

[Java] Zip 파일 생성 후 파일 다운로드(Ajax 이용) 본문

Java

[Java] Zip 파일 생성 후 파일 다운로드(Ajax 이용)

yyyyMMdd 2023. 12. 8. 13:31
728x90

#. 기존 만들어져 있는 것을 보면 1개의 탭에 최대 5개의 파일을 올릴 수 있으며 이 탭은 최대 5개까지 생성하게끔 되어있었습니다.

 

#. jqGrid의 한 행을 더블클릭할 때 저장된 데이터를 호출해 위의 폼이 나타나며, 행의 수는 많았습니다.
    (업로드 시 Key가 되는 값과 파일경로, 파일명을 DB에 저장했다가 호출)

 

#. 이 파일들을 다운로드할 때 1개씩 다운로드 해야 할 것이라는 생각이 들게 되어 확인해 본 결과 일괄 다운로드 로직은 없었습니다.

 

#. 게다가 이런 데이터가 수십 수백 개라면 다운로드하는 작업만 한참 걸릴 것이기에 뭔가 방법이 필요했습니다.

 

#. 방법은 데이터별 파일을 모아 폴더로 구성하고 그 폴더들을 모아 압축파일로 구성하여 다운로드하는 방법이었습니다.

 


  • Step 1. JSP에서 Ajax를 이용해 데이터 가져오기

    - jqGrid에서 선택한 행의 데이터를 서버로 가져와야 합니다.

    - 보통 URL을 이용해 다운로드를 하나 이 경우 선택한 데이터가 다량이라 URI에 대입해 처리하기 곤란했기에 Ajax로 처리하기로 하였습니다.

$('#download').click(function() {
    // 파일명 구분을 위한 날짜 데이터 생성
    var date = new Date()
    var today = ""+date.getFullYear() + (date.getMonth()+1) + date.getDate();
    
    // jqGrid 행 데이터 가져오기
    var obj = $('#jqGrid');
    var ids = obj.getGridParam('selarrrow');
    var id = obj.jqGrid('getDataIDs');
    var params = new Array();
    
    if(ids.length > 0) {
    	for(var i = 0; i < ids.length; i++) {
            var data = obj.getRowData(ids[i]);
            params.push(data);
        }
        
        $test.ajax({
            type : 'POST'
            ,url : 'filedownload.do'
            ,data : {data:JSON.stringify(params)}
            success : function() {
            	alert("성공 ! " + today);
            }
            ,error : function() {
            	alert("실패 ! " + today);
            }
        })
    }    
}

 


  • Step 2. 가져온 데이터를 압축파일로 만들기
@RequestMapping("fileDownload.do")
public void fileDownload(Map<String, Object> param, HttpServletResponse response) throws ParseException, FileNotFoundException, IOException {
    String jsonStr = param.get("data").toString();
    Map<String, Object> paramMap = new HashMap<String, Object>();
    List<Map<String, Object>> filepathList = null;
	JSONParser parser = new JSONParser();
	JSONArray obj = (JSONArray) parser.parse(jsonStr); 
    String uploadPath = "업로드한 폴더 경로";
    
    // 임시파일로 만듦 ("파일명(prefix)", ".확장자명(surfix)")
    File zipFile = File.createTempFile("output", ".zip");
    
    try (FileOutputStream fos = new FileOutputStream(zipFile); ZipOutputStream zos =  new ZipOutputStream(fos)) {
   	    for(int i = 0; i < obj.size(); i++) {
            JSONObject row = (JSONObject) obj.get(i);
            String key1 = row.get("key1").toString();
            String key2 = row.get("key2").toString();
            String key3 = row.get("key3").toString();
            
            paramMap.put("key1", key1);
            paramMap.put("key2", key2);
            paramMap.put("key3", key3);
            
            filepathList = DB접근 메서드(paramMap);
            
            for(int j = 0; j < filepathList.size(); j++) {
                Map<String, Object> fileInfo = filepathList.get(j);
                String fileName = uploadPath + fileInfo.get("DB 내 파일경로 컬럼") + fileInfo.get("DB 내 파일명 컬럼");
                
                try(FileInputStream fis = new FileInputStream(new File(fileName))) {
                    // 폴더 생성 및 폴더 내 파일 생성 -> / 를 기준 왼쪽 내용이 폴더를 생성함
                    // 왼쪽 내용이 이미 있는 경우 오른쪽 내용의 하위 파일만 생성함
                    ZipEntry fileEntry = new ZipEntry(key1 + "-" + key2 + "-" + key3 + "/" + fileInfo.get("구분값") + fileInfo.get("DB 내 파일명 컬럼");
                    zos.putNextEntry(fileEntry);
                    
                    // zipFile을 바이트 단위로 반환한 변수 생성
                    byte[] buffer = new byte[(int) zipFile.length()];
                    
                    int length;
                    while((length = fis.read(buffer)) > 0) {
                        zos.write(buffer, 0, length);
                    }
                    zos.closeEntry();
                }
            }
        }
        zos.finish();
    }
    
    // 임시파일 경로
    if(zipFile.exists()) {
        // 파일다운로드 설정
        response.setContentType("application/zip");
        response.setHeader("Content-Disposition", "attachment; filename=output.zip");
        response.setContentLength((int) zipFile.length());
        
        // 파일을 읽어 출력 스트림으로 전송
        try(InputStream is = new FileInputStream(zipFIle); OutputStream os = response.getOuputStream()) {
            byte[] buffer = new byte[(int) zipFile.length()];
            int bytesRead;
            while((bytesRead = is.read(buffer)) != -1) {
                os.write(buffer, 0, bytesRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    } else {
        // 파일 없을 경우
        response.setContentType("text/plain");
        response.setCharacterEncoding("UTF-8");
        response.getWriter().write("File not found.");
    }
	// 임시파일 내 zip파일 삭제
	if (zipFile.exists()) {
	    zipFile.delete();
	}
}


- putNextEntry : zip 파일 내 항목을 작성하고 인스턴스로 들어간 변수에 담긴 폴더명, 파일명으로 폴더 및 파일을 생성


- zipFile.length() : zipFile의 파일 크기를 바이트 단위로 반환

 

#. 위와 같이 처리하면 선택한 데이터의 양만큼 각각 key1, 2, 3 이름으로 구성된 폴더 내 파일을 임시파일로 생성하고 이를 zip파일로 압축한 뒤 임시파일을 삭제하게 됩니다.

#. 디버깅을 해보면 임시파일 경로는 temp폴더로 지정된 것을 볼 수 있습니다.

 


  • Step 3. jQuery 문제로 JSP 소스 추가

- 다운로드 기능을 실행하면 확장자가 zip이기 때문에 압축파일이 생성은 되나 압축 푸는 과정에서 오류가 발생했습니다.

- 이에 대해 찾아보니 Ajax로 파일을 받는 것은 불가하다는 말이 많았으나 blob을 통해 처리가 가능하다는 말을 봤습니다.

- JSP에 아래와 같이 추가하면 처리가 가능합니다.

var $test = jQuery.noConflict(true);

$('#download').click(function() {
    // 파일명 구분을 위한 날짜 데이터 생성
    var date = new Date()
    var today = ""+date.getFullYear() + (date.getMonth()+1) + date.getDate();
    
    // jqGrid 행 데이터 가져오기
    var obj = $('#jqGrid');
    var ids = obj.getGridParam('selarrrow');
    var id = obj.jqGrid('getDataIDs');
    var params = new Array();
    
    if(ids.length > 0) {
    	for(var i = 0; i < ids.length; i++) {
            var data = obj.getRowData(ids[i]);
            params.push(data);
        }
        
        $test.ajax({
            type : 'POST'
            ,url : 'filedownload.do'
            ,data : {data:JSON.stringify(params)}
            ,xhrFields : {responseType : "blob"}
            success : function(data, textStatus, xhr) {
            	var blob = new Blob([data]);
                var link = document.createElement('a');
                link.href = window.URL.createObjectURL(blob);
                link.download = "output" + today + ".zip";
                link.click();
                alert("성공 ! " + today);
            }
            ,error : function() {
            	alert("실패 ! " + today);
            }
        })
    }    
}


- 처음엔 Ajax 부분에 blob 관련된 내용만 추가하고 실행해 보니 여전히 처리되지 않았었습니다.


- 그래서 확인해 보니 blob은 jQuery 3 버전대 이상부터 사용 가능하다는 글을 봤습니다.


- 당시 사용하고 있던 jQuery는 1. 후반대 버전으로 처리가 안되던 것이었습니다.


- 그리하여 3 버전대로 파일로 변경했으나 1 버전과의 호환성 문제로 오류가 많이 발생했습니다.


- 다시 찾아보니 jQuery의 noConflict라는 기능으로 해당 ajax만 3 버전대 사용이 가능한 것을 확인하였습니다.

 


#. 복잡해 보이긴 하나 틀만 이해하면 어렵지 않을 것입니다.

#. 혼자서 하다 보니 쉬운 길을 두고 돌아온 건 아닌지 모르겠으나 이게 최선이라 생각했습니다.

#. 선택 데이터

    - 4개 선택 행 중 2개는 파일이 없는 Tab이므로 파일이 있는 데이터의 폴더 2개만 생성됐습니다.

 

 

 

 

 

===========================================================
틀린 내용이 있거나 이견 있으시면 언제든 가감 없이 말씀 부탁드립니다!

===========================================================

728x90