상황
byte[] totalByte = Files.readAllBytes(Paths.get(resourceMasterEntity.getPath()));
....
BufferedOutputStream outStream = new BufferedOutputStream(response.getOutputStream());
....
outStream.write(totalByte);
파일을 byte로 저장한 뒤,
response로 보내려는 목적으로 작성했고 실제로 작동을 했으나
서버가 터졌습니다......ㅠㅠ
이유를 알아보고자 해당 API가 동작할 때 서버 cpu와 메모리를 관찰했습니다.
.......?!?!
실제로 CPU 사용량의 %가 100을 훌쩍 넘겼으며 CPU 사용량이 비정상적일 때 나타나는
Controller 중복호출을 하는 현상까지 발생!!
원인
정확하게 파악하진 못했지만,
byte[] totalByte = Files.readAllBytes(Paths.get(resourceMasterEntity.getPath()));
에서 Files.readAllBytes에서 가져온 byte[]이 메모리에 쌓여있었기 때문!!
희안하게 null을 해줘도 메모리에 올라간 byte가 없어지질 않네요...(totalByte는 지역변수)
API 호출 할 때 마다 메모리 용량이 파일 크기만큼 커지는 Magic ㅋㅋㅋㅋㅋㅋ
(....다른 기술블로그보면 잘만 된다던데 왜....ㅁ너랴ㅣㅁㅈㄿㅁ냚ㄴ)
반년차도 안된 저는 빠르게 다른 방법을 강구해봅니다.....음....
..!!!!
해결 방법
FileInputStream fis = null;
BufferedInputStream bis = null;
//FileOutputStream fos = null;
BufferedOutputStream bos = null;
Date d = null;
try{
// FileInputStream ("파일경로") 객체를 생성 후 BufferedInputStream 객체를 생성
fis = new FileInputStream(resourceMasterEntity.getPath());
bis = new BufferedInputStream(fis);
// FileOutputStream 으로 출력할 파일 ("response.getOutputStream()") 객체 생성 후 BufferedOutputStream 객체
//fos = new FileOutputStream(response.getOutputStream());
bos = new BufferedOutputStream(response.getOutputStream());
int i = 0;
d = new Date();
long start = d.getTime();
// 1바이트씩 읽어서 버퍼에 담는다.
while((i=bis.read())!=-1){
bos.write(i);
}
d = new Date();
long end = d.getTime();
log.info("File Download - Time : {} sec", (end-start)/1000);
//bos.flush();
}catch (Exception e){
e.printStackTrace();
}finally {
// 마지막에 FileInputStream / FileOutputStream을 닫아준다.
// BufferedInputStream / BufferedOutputStream 도 닫아준다.
if(bis != null) try{bis.close();}catch(IOException e){}
if(fis != null) try{fis.close();}catch(IOException e){}
// BufferedOutputStream 이 close() 되면서 버퍼의 내용을 출력한다.
if(bos != null) try{bos.close();}catch(IOException e){}
//if(fos != null) try{fos.close();}catch(IOException e){}
}
Java에서 대용량파일을 효과적으로 전송하는 방법이 2가지 정도 있는데
그 중 한가지인 "BufferedInputStream / BufferedOutputStream"을 사용했습니다.
아무튼 생각보다 속도도 빠르고 메모리도 거의 차지하지 않는 좋은 방법으로 해결했습니다!!
*Ref
https://www.amitph.com/java-read-write-large-files-efficiently/
'Spring & Spring Boot' 카테고리의 다른 글
[SpringBoot] 원시타입, 참조타입/Boolean boolean의 차이점 및 이슈 (0) | 2022.12.28 |
---|---|
[MyBatis] resultType 정리(feat. List<DTO>로 리턴받기) (0) | 2022.12.16 |
MyBatis란? (0) | 2022.06.07 |
JSON / Ajax / REST API (0) | 2022.06.03 |
왜 Spring Framework를 선택할까? (0) | 2022.06.02 |