일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- 대용량 업로드
- 스트림
- 그리드
- docker
- apache.poi
- spring
- 제이쿼리그리드
- jqGrid
- JQuery
- Javascript
- ci/cd
- 보안
- 자동배포
- JPA
- ORM
- 자바8
- QueryDSL
- java
- stream api
- Stream
- sqlserver
- mom
- poi
- mssql
- 엑셀 업로드
- MessageQueue
- Jenkins
- 자동빌드
- rabbitmq
- DevOps
- Today
- Total
개발 메모장
[Java] Zip 파일 생성 후 파일 다운로드(Ajax 이용) 본문

#. 기존 만들어져 있는 것을 보면 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개만 생성됐습니다.


===========================================================
틀린 내용이 있거나 이견 있으시면 언제든 가감 없이 말씀 부탁드립니다!
===========================================================
'Java' 카테고리의 다른 글
[Java] Stream API(2) - 스트림 데이터 가공 (0) | 2023.12.13 |
---|---|
[Java] Stream API(1) - 스트림 데이터 생성 (0) | 2023.12.12 |
[Java] 공공데이터 - 공휴일 API 사용하기 (1) | 2023.12.06 |
[Java] OOM과 GC (Out of Memory Error와 가비지 컬렉터) (1) | 2023.12.04 |
[Java] String, StringBuffer, StringBuilder의 특징 및 차이점 (1) | 2023.11.29 |