giggs 2022. 10. 2. 17:58

 

 

 

-- INDEX --

 

 

1. 브라우저 - 다운로드 2. 서블릿 - 다운로드 3. 스프링권장 - 다운로드 4. 빌더패턴
<a download href=" "> resp.setHeader Dispatcher Servlet 이용 이너클래스
임시객체
메서드체이닝
builder
Content-Type
Content-Disposition
Content-Length
ByteArrayResource
ResponseEntity

 

 

 

 

 


 

 

 

 

파일 다운로드 받기

 

 

 

1.  브라우저 이용하여 다운받기 - < a download href=" " >

 

  • 현재 보여지고 있는 <img>를 <a>태그로 감싼 뒤
  • <a> 태그에 download 속성 추가 
  • download href는 <img> 태그의 src와 같은 경로로 작성
  • src 경로만 잘 작성해주면 된다.

 

 

 

 

누르면 자동으로 다운로드해준다.

 

 

 

# 이런식으로도 가능하겠죠 #

 

 

 

 

크롬 브라우저가 어느 곳에 다운받아서 보여주고 있는 이미지를

우리가 평소에 다운로드 받는 폴더

거기에 다시 다운로드 받게 해주는 것이다.

 

이 다운로드 작업은

크롬이 파싱 해서 진행해주는 것이다.

 

 

 

 


 

 

 

 

2. 서블릿 방식으로 다운로드 처리해보기

 

 

 

2-1 : 파일 다운로드 담당 - 컨트롤러에게 요청

  • 해당 컨트롤러는 요청받은 파일명에 맞는 파일 찾아서
  • byte단위로 처리하는 output스트림 얻어와서 보내주면 된다

 


 

 

 

 

<헤더>

  • 지금까진 데이터 덩어리로 { 키:밸류 } 형태로 text만 보냈었는데
  • 이제 Content-Type으로 - byte덩어리를 보낼 것이고
  • byte덩어리 보낼 것이라고 알려주어야 한다.

 

 

 

<바디>

  • 데이터 보내는 것이다.
  • write(~~); - 완성된 게 한 번에 쭉 전달하는 게 아니라 되는대로 보내주면서 완성되는 것이다.
  • resp.getOutputStream().write(~~~);

 

 


 

 

2-2 : 보내려는 파일에 대한 정보를 알려주어야 한다.

  • resp.setHeader

 

 

 


 

 

 

#  URLEncoder.encode.encode()  #

띄어쓰기와 물음표[?]를 제외하고

%+- 같은 특수문자 애들도 URLEncoder.encode() 이용하면 처리해준다.

한글로도 다운로드 가능하다.

 

 

 


 

 

 

2-3 : 요청받은 파일 찾아가서 output스트림으로 보내주면 된다.

  • 파일로부터 바이트 덩어리 얻어오는 부분 ( = 숫자로 얻어오는 부분)
  • while ( buf ) ~~ 하는 작업을 해야 하는데 이 작업이 힘들고 귀찮다.
  • 그래서 java가 api로 만들어놓은 것 사용하자. -- FileUtils.readFileToByteArray( );
  • 1바이트씩 처리해주는 스트림 얻어서 보내 줄 것이다. - 지금 우리는 내보내는 거니까 아웃풋 스트림으로 써준다.

 

 

 

 

 

 


 

 

 

 

3. 스프링에서 권장하는 다운로드 방식

 

# DispatcherServlet

# ResponseEntity :

  • 응답 정보들이 모여있는 객체 ( vo나 dto랑 비슷한 개념으로 받아들이면 됩니다. )

 

 

 

 

3-1 :DispatcherServlet에게 전달해서 처리하도록 해주기

  • 컨트롤러에서 직접 resp 객체로 작업했었는데
  • 여기서는 DS에게 전달해서 처리하도록 해줄 것이다.

 

 

 

 

 

# 그러기 위해서는 ?

  • java가 api로 만들어놓은 -- FileUtils.readFileToByteArray( );로 바이트 배열로 파일 읽어오고
  • 바이트 배열 단위 자체로서 전달하면 안 되고 어떤 객체로서 감싸주고 전달해 주어야 한다.
  • 어떤 객체? = ByteArrayResource
  • ByteArrayResource 객체로 만든 뒤 리턴하면 된다.

 

 

 

 


 

 

3-2 : ResponseEntity 사용해서 객체 return 해주기

  • 컨트롤러에서 view페이지로 리턴하기
  • 원래 return 해주면 여기 있는 view페이지로 포워딩해라 알려주었었다.
  • 근데 지금 전달해주는 것은 응답할 객체 자체이다.
  • 내가 지금 어떤 객체를 줄 건데 이거 자체를 응답해라 ( = 응답 객체를 그대로 보여줘라 )
  • 해주려면 ResponseEntity 객체 사용해야 한다.
  • 빌더 패턴을 사용하여서 만들어지는 객체인 ResponseEntity

 

 

 


 

 


 

 

3-3 : 코드 확인

 

 

 

 

 

 

 

 


 

 

 

 

4.  빌더 패턴

 

 

 

 

 

태어날 때부터 값을 가지고 태어나지 않으면

멀티스레드 환경에서 문제가 생길 수 있겠구나.

 

태어날때부터

모든 값을 가지고 태어날 수 있게 해 주기 위해서

파라미터로 다 넣어주고 만들어주자..

 

파라미터가 40개되면?

 

앞으로는

세터의 장점과

생성자의 장점

합친 빌더 패턴으로 만들 것이다.

 

안정적이며 편안한 방법!

 

 


 

 

 

4-1 : 빌더패턴 사용법(=흐름 이해)

 

 

#빌더 패턴 탄생 이유

  • 값을 가지고 태어나게 해주고 싶다. - 매번 값을 순서에 맞춰 넣어주기 힘들다. 값이 많아지면 채워주기도 힘들다.
  • 태어난 뒤 값을 부여한다.  - 값을 넣어주지 못하는 경우가 생긴다.
  • 이런 문제를 해결하기 위해 나온 것이 빌더 패턴

 

 

#빌더 패턴 세팅 ( 임시 객체 파라미터로 받는 기본 생성자 세팅 )

  • 객체를 생성할 때 - 생성자 파라미터로 값을 하나하나 받아오는 것이 아닌 임시 객체를 받아온 뒤
  • 임시 객체가 가지고 있는 값으로 객체의 값 세팅해주고 태어나도록 생성자 세팅

 

 

하고 난 뒤

 

 

 

# 메서드 체이닝 이용하여 필드 값 채워주기 

  • 생성하려는 객체와 임시 객체를 연결시켜서 임시 객체로 내가 생성하려는 객체를 만들어주기.
  • 방법은? -
  • inner클래스로 연결시켜놓고
  • 메서드체이닝 이용하여 임시 객체에 값들을 하나하나 세팅해 나가면서 

 

# build 메서드로 세팅해놓은 기본 생성자 호출

  • 세팅이 끝나면 build메서드를 통해
  • 자기 자신(임시객체)을 파라미터로 받는 생성자를 호출! 
  • 이 생성자는 임시 객체가 가지고 있는 값으로 자신의 값 세팅해주도록 위에서 구현하였다.

 

 

 


 

 

 

# 밑에 부분은 추가 정리 필요 #

 

 

객체가 태어났는데 값이 없는 게 문제이다.

해결하기 위해서는 생성자 부분에서 값을 받아서 태어나게 해 주면 된다.

 

값을 가지고 태어나는 것은 좋은데

이렇게 하면 순서만 가지고 판단하기 어렵고 힘들다.

 


 

 

a,b,c 각각 파라미터 값으로 넣어주는 것이 아닌 

a,b,c의 값을 가지고 있는 임시 객체를 파라미터로 넣어준다.

임시 객체인 tempVo를 내 마음대로 세터로 만드는 것

(= 임시 객체의 메서드를 setA(), setB() 이런 식이 아니라 메스명을 a, b 해놓고 - 내용은 setter로 구현하는 식 )

 

 

 


 

 

 

임시 객체가 아예 상관없이 따로 있는 것이 아니라 서로 관계가 있게 해주어야한다 

왜? -

원래의 객체로 임시객체 생성할 수 있는 방법도 확보하려고 ( MemberVo().tempVo(); )

임시 객체로 원래의 객체를 생성할 수 있도록 해주어야 한다 (= build{ new MemberVo(this) })

 

어떻게?

inner Class 로 만들어주었다. ( 그전에 인터페이스 과정이 있긴 했지만 최종 사용하는 것으로 설명 )

 

 

 

inner class를 static class로 만들어주는 이유는? 

내부 객체는 외부 객체가 만들어진 다음에 만들어질 수 있다.

그렇게 해 놓으면 부수적으로 따라오는 조건들이 많이 생긴다.

따라서 외부 객체의 생성 여부와 상관없이 내부 객체가 만들어질 수 있도록

inner Class에 static 부여

 

 


 

# 메서드 체이닝 체크

 

tempVo.setA(10) 메서드를 호출하면

a에 값에 10을 세팅하고 return 하는 것은 this 자기 자신이다. 

a값이 세팅된 tempVo를 리턴하는 것이다.

리턴한 것이 tempVo이니까 이어서 tempVo.setB(20) 이런 식으로도 사용 가능한 것이다!

 

 

메서드명은 setA setB 이런 식이나 setMemberId() , setMemberPwd() 이런것이 아니라

그냥 A, B 이런식이나 memberId(), memberPwd() 이런 식으로 만들어 놓고 호출! - 내용은 위의 내용처럼

x. memberId(10) 이 실행되면 - 멤버 id에 10이 세팅된 TempVo x 가 리턴되어서 올 것이고

이 x.memberPwd(20)이 실행되면 - 멤버 pwd에 20이 세팅된 TempVo x가 리턴되어서 올 것이고

 

이  x에다가 다시-다시- 쭉 하다가 마지막 build() 메서드를 호출

 


# build() 메서드 체크 

위의 메서드 체이닝 과정으로 - 값 세팅을 끝낸(값 세팅이 쌓인) 자기 자신을 파라미터로 받는

MemberVo 생성자 호출 ---> 값 세팅된 MemberVo 객체 생성해서 리턴

 

 


 

# 빌더에서 호출되는 MemberVo 생성자 체크