본문 바로가기

JAVA

[JAVA] POI 엑셀파일 생성시, "XXX의 내용에 문제가 있습니다."

728x90
반응형

JAVA POI 엑셀파일 생성시, "이 통합 문서의 내용을 복구하시겠습니까?" 에러 해결보자


 

최근 java POI를 사용하여 엑셀업로드 개발을 작업하고 있는데, 한가지 문제를 만났다.

엑셀 파일을 생성 후 아래 코드처럼 구현하였는데,

 

response.setHeader("Set-Cookie", "fileDownload=true; path=/");
response.setHeader("Content-Disposition", String.format("attachment; filename=" + excelName));

response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
wb.write(response.getOutputStream());

 

이때 다운로드받은 파일을 열면 "파일 손상"이라는 에러가 발생하였다.

 

+ 어떤 에러?

 

[파일명]에 읽을 수 없는 내용이 있습니다. 이 통합 문서의 내용을 복구하시겠습니까? 이 통합 문서의 원본을 신뢰할 수 있는 경우 [예]를 클릭하십시오. 

 

이 상태로 [예]를 누르게 되면 파일이 열리면서 아래 메세지가 적힌 팝업이 뜹니다

 

[파일명.xlsx](으)로 복구

읽을 수 없는 내용을 복구하거나 제거하여 파일을 열 수 있습니다.

 

 

[복구] 버튼을 누르면 엑셀파일은 알맞게 생성되었지만 열때마다 뜨는 경고창을 해결해야했다.

 

 

ByteArrayOutputStream bout = new ByteArrayOutputStream();
wb.write(bout);
bout.close();

response.setHeader("Set-Cookie", "fileDownload=true; path=/");
response.setHeader("Content-Disposition", "attachment; filename=" + excelName);
response.setContentLength(bout.size());

ServletOutputStream out = response.getOutputStream();

out.write(bout.toByteArray());
out.flush();
out.close();

 

위 코드처럼 변경한 결과, 문제는 바로 해결되었다.

하지만 stream에 무지했기 때문에 위 코드를 보면서도 어떤 점 때문에 해결되었는지 알 수가 없었다.

 

하나하나, 모르는걸 알아가면서 원인을 찾아내보자.

 

(1) ByteArrayOutputStream

=> 내부적으로 저장 공간이 있어 출력되는 모든 내용들이 내부적인 저장 공간에 쌓이게 된다.

=> 메모리의 특정 객체에 byte 배열을 쓴다.

 

(2) response.setContentLength(bout.size());

=> 파일의 크기

 

(3) ServletOutputStream

=> 파일을 브라우저에 출력하기 위해 사용한다.

=> 추상클래스로서, 인스턴스를 생성할 수 없다.

 

(4) out.write(bout.toByteArray());

=> 브라우저에 출력할 파일

 

.

.

저 코드를 분석해본 이후, 나는 내가 Stream에 대한 JAVA 기초가 부족하다고 느꼈다.

 

response.setHeader("Set-Cookie", "fileDownload=true; path=/");
response.setHeader("Content-Disposition", String.format("attachment; filename=" + excelName));

response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
wb.write(response.getOutputStream());

 

내가 처음 사용했던 위 코드에서 단지 OutputStream을 close해주지 않았기 때문이였다.

response.getOutputStream().close() 한줄 때문에 엑셀 파일에서 내용을 손실했다는 에러가 발생한 것이였다.

 

 

내가 위 코드를 아래 코드로 수정하였는데, 아래코드도 분석한 결과 몇가지 문제가 있었다.

ByteArrayOutputStream bout = new ByteArrayOutputStream();
wb.write(bout);
bout.close();

response.setHeader("Set-Cookie", "fileDownload=true; path=/");
response.setHeader("Content-Disposition", "attachment; filename=" + excelName);
response.setContentLength(bout.size());

ServletOutputStream out = response.getOutputStream();

out.write(bout.toByteArray());
out.flush();
out.close();

 

위 코드로 실행했을때 물론 내게 발생되었던 엑셀파일 파일손상 에러는 해결되었다.

위 코드를 다시 보자.

 

ByteArrayOutputStream 을 생성하고, wb.write(bout)로 해당 스트림에 wb객체 (엑셀파일)을 write한다.

(여기서, WorkBook 객체 wb의 write 메소드는 wb 객체에 쓰는게 아닌, bout 안에 자신을 쓴다는 뜻이다. = write out)

그리고 다시한번 ServletOutputStream 을 생성하여 ByteArrayOutputStream 스트림 안의 내용을 wirte 한다.

 

위 코드는 Stream을 굳이 2번 생성한 것이다.

1. ByteArrayOutputStream을 선언하고,

2. ByteArrayOutputStream스트림에 WorkBook 객체 wb를 쓰고, 

3. 또다시 ServletOutputStream을 선언하고, --> Stream 2번 생성

4. 해당 스트림에 ByteArrayOutputStream 의 객체를 toByteArray 메소드를 사용하하여 write 한것이다.

 

결론적으로 맞는 코드는,

 

아래 코드처럼, response.getOutputStream()으로 해당 스트림에 WorkBook 객체 wb를 쓰고,

response.setHeader("Set-Cookie", "fileDownload=true; path=/");
response.setHeader("Content-Disposition", String.format("attachment; filename=" + excelName));

response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
wb.write(response.getOutputStream());

 

마지막에 꼭 response.getOutputStream().close()로 해당 스트림을 close 해주는 것이다.

 

 

여기서 한가지 더,

 

아마 flush()를 선언해주고 close()를 선언하는 개발자가 있을것이다.

 

위 코드를 분석하면서 한가지 알아낸 사실은, close() 하나만 선언해도 선언되지않은 flush() 메소드도 실행된다는 것이다.

728x90
반응형

'JAVA' 카테고리의 다른 글

[JAVA] HTTPS 요청 시 SSL 인증서 오류 무시하기  (2) 2020.08.06
[JAVA] 이미지 업로드하기  (0) 2020.06.12
[Java] 객체지향 생활 체조  (0) 2020.02.25
[Java] Rest API  (0) 2020.02.21
[Java] DAO,DTO,Entitiy Class  (0) 2020.02.16