개발/Spring

SpringFramework를 이용한 파일 다운로드

신매력 2015. 4. 13. 19:18

어떤 컨트롤러(URL)를 호출하면 바로 '파일 다운로드'로 파일을 다운로드 할 수 있게 하려고 한다.



그 전에 알아야 할 개념

아주 간단히 설명하고 넘어가겠음.

원론적인 설명이 아니라 이 포스팅을 설명하기 위한 좀 다른 시선의 얕은 설명이다.ㅎㅎ

(아는 분들은 Pass)


 * MIME (Multipurpose Internet Mail Extensions)


문자열을 전송할 때는 7비트 아스키파일로 전송하여 사용하지만,

더 큰 음악, 사진 등의 파일을 보내는 경우는 8비트 데이터로(바이너리 데이터) 사용한다.

이 것을 전송하기 위해서는 바이너리 데이터를 텍스트로 변환하는 인코딩 작업이 필요하다.


MIME은 이런 인코딩 방식의 일종이다.

이메일 시스템을 통해 파일을 전달하도록 개발되었다.

특정 데이터 타입을 받아 인코딩 한다.


즉, MIME은 인코딩 + data type(contents type) 이다.


데이터 type의 예를 들면 문자열이면 UTF-8이라던가,

미디어 type이면 h.264 divx 이런 것이 data type이다.



* 바이너리 파일(Binary File) 


컴퓨터가 이해할 수 있는 비트들의 연속으로 조합된 데이터.


예를들어, jpg 파일을 form을 통해 들고다니거나

업로드를 구현한다던가 할 때 contents type을 명시한다.

http://www.sitepoint.com/web-foundations/mime-types-complete-list/

(위의 사이트에는 MIME Type 표준들이 명시되어있다)


여기 명시된 타입을 사용하면, 이 파일이 어떤 형태의 타입인지 알 수 있고,

표준처럼 사용을 할 수 있다.


그런데 바이너리 파일로 넘기면, 그 데이터 타입이 무엇인지 여부 상관없이

컴퓨터가 이해할 수 있는 비트로 된 그 파일을 통째로 넘기는 것이다.

순수한 파일 그 자체이다.

모든 파일은 바이너리로 되어있다.


MIME type은 사람들이 사용하기 쉽도록 이미 알고 있는 컨텐츠타입을 명시해서 

어떤 종류의 파일임을 알고 사용할 수 있도록 한 것.




이번에 내가 구현한것은

1. 어드민이 다른 서버에서 파일을 받아오고

2. 그것을 어드민에서 txt 파일을 다운로드 시키는 것

인데


바이너리로 받아오려면 어떻게 해야할까??


Spring의 RestTemplate을 이용해서 Server to Server call을 할 때 

아래와 같이 사용해서 바이너리 데이터를 받아온다.


HttpHeaders headers = new HttpHeaders();

headers.setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM));

headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);

HttpEntity<String> entity = new HttpEntity<String>(headers);

ResponseEntity<byte[]> restTemplateResponse = 

      restTemplate.exchange(uri, HttpMethod.GET, entity, byte[].class);


MediaType.APPLICATION_OCTET_STREAM 은 

표준으로 정의되어있지 않은 파일인 경우 지정하는 타입이다.

octet stream이 8비트로 된 데이터이니, 순수한 바이너리 데이터로 받아온다는 의미이다.


받는 것은 byte 배열로 받으면 된다.




이제 바이너리 파일을 받았으니, 

어드민 페이지에서 사용자가 txt 파일로 다운로드 받게 할 차례다.


@RequestMapping("/get_file")

public void getFile(HttpServletRequest request, HttpServletResponse response) {

   // server to server call

   // get binary file 


   response.setContentType("text/plain");

   response.setHeader("Content-Disposition", "attachment;filename=result.txt");

   ServletOutputStream sos = response.getOutputStream();

   IOUtils.write(restTemplateResponse.getBody(), sos);

}


text 파일이기 때문에 컨텐츠타입을 text/plain 으로 정의했다.

다른 파일이면 다른 컨텐츠타입을 명시한다.


가장 핵심인 setHeader에 "Content-Disposition", "attachment;filename=result.txt" 를 써준다.

Content-Disposition 은 브라우저에서 다운로드 창을 띄우는 역할을 하며,

뒤에 파라미터를 통해 파일 이름과 확장자를 지정한다.


그다음은 스트림 파일을 통해 write 해주면 끝..



처음에는 @ResponseBody를 붙이고 File을 리턴하고

메시지 컨버터 설정을 해야하나 했는데

저렇게 response에 타입 지정하니까 잘 된다.


바이너리라 그런지 한글도 깨지지 않고 잘 나온다 후훗