스프링에서 지원하는 다운로드를 쓰려면 아래와 같이 하시오.!

일단 기본적으로 파일 다운로드 처리하기에 앞서서 알아야 할 사항을 배워보도록 하자.

1. 파일링크를 클릭할때 컨트롤러 클래스에게 파일패스와 파일이름을 던져주고 

2. 받은 컨트롤러 클래스에서 그 파일 패스와 파일이름으로 file 을 만들어서 (DownloadController)

3. 뷰로 전달을 할 것이다. 

4. 그럼 뷰에서 받은 file 정보를 이용해서 실제 파일을 읽어들인 다음 원하는 위치에 쓰는 작업을 한다. (DownloadView)

 

 

일반적인 컨트롤러 클래스에서 작업을 한 후, 뷰 페이지로 결과값을 뿌려주는 것인데

일반적인 뷰페이지는 JSP 페이지였다.

하지만 다운로드에 사용될 뷰는 JSP 가 아니라  클래스 파일이다. 



그렇기 때문에 아래처럼 일반적으로 사용하던 viewResolver 가 처리하는 것이 아니라

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">

<property name="prefix" value="/"/>

<property name="suffix" value=".jsp"/>

<property name="order" value="1"/>

</bean>  






download 만을 처리하는 viewResolver 가 따로 존재해야 한다. 여기에는 id값이 없다...주의할것!!!!!!!!!

<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">

      <property name="order" value="0"/>

</bean>


주의할 점은 위 두 코드에 포함된 프로퍼티를 보면 order 라는 프로퍼티가 있는데

이 프로퍼티는 두개이상 존재하는 viewResolver 를 위해서

우선순위를 매기는 것이다. 만약 우선순위를 명시 하지 않으면 "가장 낮은 우선순위를 갖게 된다."

우선순위는 "0"이 가장 먼저 실행되고, 이후로 매겨지는 순서에 따라 실행된다.


다음 viewResolver가 실행되는 기준은 "null" 이 반환되느냐 가 기준이다. 

그렇기 때문에 널값이 있을수 없는 InternalResourceViewResolver 가 우선순위가 높게 되면,
다른 viewResolver 는 사용되지 않게되는
문제가 있다. (항상 뷰 이름에 매핑이 되는 뷰 객체를 리턴하기 때문)

 그래서 InternalResourceViewResolver 은 우선순위가 가장 낮아야 한다.





그러면 이제 BeanNameViewResolver 를 사용하는 법을 알아 보자


BeanNameViewResolver (파일 다운로드 viewResolver)

"null" 이 반환되지 않는다면, (즉 컨트롤러 클래스에서 리턴되온 뷰페이지 값과 일치하는 빈이 있는 경우)


컨트롤러 클래스에서 리턴되온 뷰페이지 값과 일치하는  빈이 등록되있는 경우는 그 빈에 해당하는 컨트롤러 클래스가

파일 다운로드를 처리하게 된다.  

그렇기 때문에 컨트롤러 클래스에서 viewResolver 로 던져줄 뷰페이지 이름과, 처리할 View 클래스 빈이름이 같아야 한다. 
(이말을 반대로 하자면, 실제 jsp 가 보여져야될 때는 리턴값과, view 빈 이름이 같아서는 절대 안된다.)
 

<bean id="download" class="Spring.DownloadView"/>
    - 이 코드가 다운로드를 처리할 뷰 클래스를 등록하는 것이다.
       저기 id="download" 라고 되있는 부분과, 클래스에서 리턴된 값이 같아야 한다.



그리고 url 을 처리할 컨트롤러 클래스도 등록되야되겠지.

<bean id="down" class="Spring.DownloadController"/>




여기 까지가 좀 복잡하지만 servlet.xml 파일을 설정하는 부분이다. 
하나하나 천천히 다시 읽어보면 이해가 될 것이다.






이제 처음부터 하나씩 따라가보자.


파일 이름에 링크를 걸어서 컨트롤러 클래스로 넘기는 부분부터 시작

<a href="/Spring_margo/download.do?path=${path }&fileName=${itemBean.fileName }" >       ${itemBean.fileName }      </a>

download.do 로 파일네임과 패스를 넘기게 되어있다. 







그럼 저 url 을 처리하는 컨트롤 클래스는 아래와 같다.
<bean id="down" class="Spring.DownloadController"/>
@Controller
public class DownloadController implements ApplicationContextAware{

	private WebApplicationContext context = null;
	
	@RequestMapping("download.do")
	public ModelAndView download(@RequestParam("path")String path, 
                                  @RequestParam("fileName")String fileName){
		
		String fullPath = path + "\\" + fileName;
		
		File file = new File(fullPath);
		
		return new ModelAndView("download", "downloadFile", file);
	}

	@Override
	public void setApplicationContext(ApplicationContext arg0)
			throws BeansException {
		// TODO Auto-generated method stub
		
		this.context = (WebApplicationContext)arg0;
		
	}
	
}

@RequestMapping("download.do") 어노테이션으로 지정해준것처럼
download.do 가 들어오면 저 메소드가 동작한다.
링크에서 준것처럼 패스와 파일네임을 받아서

파일에 조합해서 쓰고 "download" 뷰페이지로 파일을 "downloadFile"이름으로 삽입하고 리턴시킨다.

그러면 <bean class="org.springframework.web.servlet.view.BeanNameViewResolver">

저 viewResolver 가 먼저 리턴을 처리하려 할 것이다.

"download"로 등록된 빈이 있는지 찾아 보는데 우리는 아까 "download"로 뷰클래스를 등록시켜 놓았다. 
<bean id="download" class="Spring.DownloadView"/>

이제 DownloadView.java 클래스가 뷰페이지로 동작할 것이다.
뷰페이지에서는 map 에 등록된 파일을 이용해서 encoding 설정과 헤더설정을 해준 후 파일을 지정위치에 쓴다.

import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.util.FileCopyUtils;
import org.springframework.web.servlet.view.AbstractView;

public class DownloadView extends AbstractView {


	public void Download(){
		
		setContentType("application/download; utf-8");
		
	}
		
	@Override
	protected void renderMergedOutputModel(Map model,
			HttpServletRequest request, HttpServletResponse response) throws Exception {
		// TODO Auto-generated method stub
		
		File file = (File)model.get("downloadFile");
		System.out.println("DownloadView --> file.getPath() : " + file.getPath());
		System.out.println("DownloadView --> file.getName() : " + file.getName());
		
		response.setContentType(getContentType());
		response.setContentLength((int)file.length());
		
		String userAgent = request.getHeader("User-Agent");
		
		boolean ie = userAgent.indexOf("MSIE") > -1;
		
		String fileName = null;
		
		if(ie){
			
			fileName = URLEncoder.encode(file.getName(), "utf-8");
						
		} else {
			
			fileName = new String(file.getName().getBytes("utf-8"));
			
		}// end if;

		
		response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\";");
		
		response.setHeader("Content-Transfer-Encoding", "binary");
		
		OutputStream out = response.getOutputStream();
		
		FileInputStream fis = null;
		
		try {
			
			fis = new FileInputStream(file);
			
			FileCopyUtils.copy(fis, out);
			
			
		} catch(Exception e){
			
			e.printStackTrace();
			
		}finally{
			
			if(fis != null){
				
				try{
					fis.close();
				}catch(Exception e){}
			}
			
		}// try end;
		
		out.flush();
		
	}// render() end;
}


여기까지가 

파일링크를 클릭해서 다운로드를 하는 일련의 과정을 코드로 풀어논 것이다.

복잡해 보이지만, ....복잡하다.......죄송;




열심히 뚝딱뚝닥 거리다보면 위그림처럼 다운로드가 될것이다!!!!!!!!!


YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST
  1. 로스비 2011.08.17 20:05 신고  댓글주소  수정/삭제  댓글쓰기

    저번 처럼 이상하게 건들지도 않은 ListImpl이 오류가 생깁니다.

  2. 메로메로 2012.10.03 18:46 신고  댓글주소  수정/삭제  댓글쓰기

    지나가다가 잘배우고 갑니다. 많은 도움이 되었습니다. 이제 완전 초보인데요.

    이클립스를 세팅중인데요, 보리마고님의 이클립스를 보면 검은바탕까진 저도 설정을 햇는데, 나머지 글씨를 파란색과 형광색으로 하고 싶은데 방법좀 알려주시면 정말 감사하게습니다. ㅠ

    • 보리마고 2012.10.08 17:53 신고  댓글주소  수정/삭제

      이클립스 세팅이 아니라, 티스토리에 포스팅할때 하이라이팅 기능을 써서 코드를 저렇게 보이게 한겁니다. 이클립스는 코드 하이라이트 플러그인이 있으니 여러가지 찾아보셔서 마음에 드시는걸로 하세요 ㅎㅎ

  3. eugene 2014.05.07 12:19 신고  댓글주소  수정/삭제  댓글쓰기

    1.png가 나오는 이유는 무엇인지 궁금합니다
    직접 뷰로 값을 넘겨주는 건가요? 예제대로 하고 있는데 파일네임에 계속 null이 잡히네요

  4. kim 2014.05.30 11:31 신고  댓글주소  수정/삭제  댓글쓰기

    혹시 위 소스를 war 파일로 좀 받을수있나요?

  5. kim 2014.05.30 11:32 신고  댓글주소  수정/삭제  댓글쓰기

    가능하시다면 hg97804@naver.com
    으로 부탁좀 드리겟습니다

  6. Nathan 2015.01.16 10:30 신고  댓글주소  수정/삭제  댓글쓰기

    큰도움이 되었습니다. 감사합니다!

  7. LHN 2015.07.08 16:21 신고  댓글주소  수정/삭제  댓글쓰기

    정말 잘 배워 가요 ㅠ-ㅠ! 감사합니다.

  8. 오늘은진돗개 2015.11.25 15:00 신고  댓글주소  수정/삭제  댓글쓰기

    안녕하세요.
    궁금한게 있는데요.

    download.do에서 파일정보 데이터를 쿼리로 가져와서 다운받게하려고 하는데 파일 안만들고 가능한가요?
    File file = new File(fullPath);
    return new ModelAndView("download", "downloadFile", file);

    이거 대신 return new ModelAndView("download", "downloadFile", binary); 이런식으로요




스트럿츠2에서 파일을 다운로드하기 위해 몇 가지 미리 알아야 할 것이 있다.
다운로드를 위해 스트럿츠2는 "stream"방식의 리절트 타입을 사용한다. 

"stream" 리절트 타입을 사용해 서버에 존재하는 텍스트 파일, XML 파일을 다운로드하고
그 내용을 화면에 출력하거나 디스크 상에 저장하는 방법을 제공한다.

다운로드 기능을 구현하기 위해 다운로드 액션 클래스를 정의하고, 액션 클래의 필수 또는 옵션 프로퍼티를 정의해야 한다.
또한 이렇게 정의된 프로퍼티에 매핑하는 리절트의 파라미터를 정의하는 방법을 알아야 한다. 

 
다운로드 액션 클래스에서 매핑되어야 할 프로퍼티

private String localFileName;
private String serverFileName;	
private String contentDisposition;
private InputStream duck;
private long contentLength;


위코드와 매핑될 Struts.xml 파일에는 아래와 같이 코딩되야한다.

		
	

		${contentType} 
		${contentLength}
		${contentDisposition}
		duck
		${4096}
	
	

</param> 이 왜 출력이 안되는지 ;; 어쨋든 저뒤에 다있음. 닫아줘야함.


위 두 코드를 작성하고나면 이제 뷰페이지에서 다운로드할 파일을 클릭하면
스트럿츠에서 그 액션을 받아서 액션클래스로 연결시켜서 다운로드를 할수있게 된다.

뷰페이지에서는 대충 그냥 만든다.

		
				

			


itemBean.fileName에서 꺼낸 파일이름으로 링크가 달려서
그 링크를 클릭하게되면 url id ="download" 이름으로 달려있는 액션 "download"액션이 실행된다.


여기 까지의 동작을 정리해보면

1. 톰캣이 로딩되면서, struts.xml 파일을 읽어들여서
   다운로드에 대한 설정을 하게되고,

2. 다운로드할 파일에 액션링크가 걸리게되고,

3. 클릭하는순간 url 에 정의된 액션으로 실제 파일이름이 파라미터로 전달되면서

4. 액션클래스가 실행된다. 


이제 액션 클래스를 보자.
public class FileDownloadAction extends ActionSupport{
	
	private String localFileName;
	private String serverFileName;	
	private String contentDisposition;
	private InputStream duck;
	private long contentLength;
	
	
	public String execute() throws Exception{
		
		String basePath = getText("path");
		String fullPath = basePath + "\\" + localFileName;
		System.out.println("FileDownloadAction -> fullPath : " + fullPath);
		
		
		//contentLength
		File file = new File(fullPath);
		this.setContentLength(file.length());
		
		
		//contentDisposition
		//this.setContentDisposition("attachment; filename="
		//		+ URLEncoder.encode(localFileName, Constants.CHARSET));
		//this.setContentDisposition("attachment; filename="
		//		+ localFileName);
		this.setContentDisposition("attachment; filename="
				+ new String(localFileName.getBytes("euc-kr"),"8859_1"));
		


		System.out.println("FileDownloadAction -> ContentDisposition : " + getContentDisposition());
		
		
		//inputStream
		//this.setDuck(ServletActionContext.getServletContext().getResourceAsStream(fullPath));
		this.setDuck(new FileInputStream(fullPath));
		return "success";
	
		
	} // execute() end;

위 코드중에 주석이 덕지덕지 붙은 코드가 있는데 저코드가, 한글 변환에 관한 설정들인데,
페이지 설정에 따라서 한글설정이 다다를수 있으므로, 실험을 해봐야한다.
디비에 저장되있는 파일네임을 읽어와서 쓸때도, 한글변환설정이 맞아야 올바르게 다운로드가 된다.

YOUR COMMENT IS THE CRITICAL SUCCESS FACTOR FOR THE QUALITY OF BLOG POST