Java

[JAVA + Vue.js]엑셀(EXCEL) 템플릿(서식) 다운로드 기능 구현 방법

관리자 페이지를 개발하다보면 엑셀 업로드를 구현해야하는데
이때 기본적으로 제공하는 엑셀 서식(템플릿) 다운로드 기능을 제공해야한다.

 

소스 폴더 중에 src>main>webapp 폴더 아래에
excelTemplate 폴더를 생성해주고 엑셀 템플릿을 추가해주었다.

 


Vue.js에서 혹시나 될까? 라는 생각으로 jsp 구현방식으로 동일하게 처리를 해보았다.

vuejs에서 해본 경험이 없음으로 시도해보는 것이다.

정상적으로 처리되지않았다.

excelTemplateDn() {
	document.location.href = '/excelTemplate/excel_upload_template(USER).xlsx';
}

 

indexj.js 라우터 설정쪽에 예외처리를 추가해주었다.

router.beforeEach(async (to, from, next) => {
  if (to.path === '') {
    next('/login');
    return;
  }
  if (to.path === '/login' || to.path.indexOf('/excelTemplate') === 0) {
    next();
    return;
  }
}

 

정상적 url 호출은 되었지만 

HTTP status 코드가 200이 아니라 304가  떨어졌다. 


 

상태코드 300 – 399 : 리다이렉션 상태 코드

클라이언트에 요청에 대해 적절한 다른 위치를 제공하거나, 대안의 응답을 제공한다.

  • 300 : 클라이언트가 동시에 여러 응답을 가리키는 URL을 요청한 경우 응답 목록과 함께 반환된다.(ex] 어떠한 HTML에 문서에 대한 영문페이지와 불어페이지를 요청)
  • 301 : 요청한 URL이 옮겨졌을 때 사용. 옮겨진 URL에 대한 정보와 함께 응답되어야 한다.
  • 302 : 301과 동일하나, 클라이언트는 여전히 옮겨지기전 URL로 요청할것을 의미
  • 303 : 요청받은 행동 수행을 위해서는 다른 URL로 요청 해야함을 의미
  • 304 : 이전의 동일한 요청과 비교하여 변화가 없음을 의미(단시간에 반복된 동일 요청에 대한 대응 코드)
  • 305 : 직접적인 요청이 아니라 반드시 프락시(우회경로)를 통해 요청되어야 함을 의미
  • 307 : 302와 동일하며, HTTP Method도 변경없이 요청하여야 함을 의미

 

304 Not Modified

클라이언트(대부분 '웹브라우저'라고도 함)가 웹서버에게 어떤 파일을
요청할때,

자신의 HDD 공간에 있는(cached) 파일의 정보, 그러니깐
마지막으로 변경된 시간이나 파일크기 등등의 정보를 포함해서
서버에게 요청합니다.(요청헤더에 붙음)

서버는
클라이언트가 보낸 요청헤더 정보를 보고, 자신의 웹서버에 있는
파일 정보와 비교합니다.
이때 파일 변경시간과 크기등이 서로 같으면 서버는 304 로 응답합니다.

즉 이것은 클라이언트에 있는 파일과 서버에 있는 파일이 서로
같으므로 클라이언트 HDD 에 있는 파일을 사용하라 라는 의미이고
실제로 파일 전송은 이루어지질 않습니다.


-출처 : http://www.linuxchannel.net/board/read.php?table=qna&no=3948

 

그래서 혹시나 localhost여서 그런가 싶어서
테스트서버에 배포 후 시도해보았다.

결과는 성공이었다.

로컬호스트에서만 제대로 동작하지 않았다.

다행히 로그인 하지 않아도 접근되는 문제는 발생하지 않았다.

 

기술검토 중에 두번째 방식으로 아래와 같은 또 다른 접근방식으로 접근해보았지만
“파일 없음” 으로 떨어진다. 파일을 못 찾는 것일까?

excelTemplateDn() {
  const downloadAnchor = document.createElement('a');
  downloadAnchor.setAttribute('href', '/excelTemplate/excel_upload_template(USER).xlsx');
  downloadAnchor.setAttribute('download', 'excel_upload_template(USER).xlsx');
  document.body.appendChild(downloadAnchor);
  downloadAnchor.click();
  document.body.removeChild(downloadAnchor);    
}


 

스프링 시큐리티쪽에서 막고 있어서 그런가? 싶어 context-security.xml 파일을 열고
access 접근 방식을 permitAll로 추가해 주었지만 동일하게 실패했다.

<s:intercept-url pattern="/excelTemplate/**" access="permitAll"/>

 

첫번째 방식으로 처리되었으니 넘어가자!!

다음과 같이 서버에 접근해서 파일 다운로드 하는 방법도 있다.

@RequestMapping(value = { "/downloadFile.do", "/down/downloadFile.do" })
public FileDownloadService downloadFile(HttpServletRequest request, ModelMap model) throws Exception
{
    model.put("FILE_PATH", request.getParameter("file_path"));
    model.put("FILE_NAME", request.getParameter("file_name"));

    return new FileDownloadService();
}
public class FileDownloadService extends AbstractView {
	protected Log log = LogFactory.getLog(this.getClass());
	
	@Override
	protected void renderMergedOutputModel(Map<String, Object> model, HttpServletRequest req, HttpServletResponse res) throws Exception 
	{
		String filePath = req.getParameter("file_path");
		String fileName = req.getParameter("file_name");

		if (filePath == null || "".equals(filePath)) 
		{ 
			fileNotExists(res);
		} 
		else 
		{ 
			fileDownload(req, res);
		}
	}

	private void fileDownload(HttpServletRequest req, HttpServletResponse res) throws IOException
	{
		String filePath = req.getParameter("filePath");
		String fileName = req.getParameter("fileName"); 
	
		OutputStream out = null;
		FileInputStream read = null;
		String fullPath = req.getSession().getServletContext().getRealPath(filePath + fileName);
		
		try 
		{
			read = new FileInputStream(fullPath);
		} 
		catch(Exception ex) 
		{
			fileNotExists(res);
			return;
		}
		
		try 
		{
			int iRead = 0;
			byte[] buff = new byte[4096];

			res.setContentType("application/octet-stream; charset=UTF-8");
			res.setHeader("Content-Disposition", "attachment; filename="" + URLEncoder.encode(fileName, "utf-8") + "";");
			
			out = res.getOutputStream();
			
			while(read != null && (iRead = read.read(buff)) > 0) 
			{
				out.write(buff, 0, iRead);
			}
		} 
		catch(Exception ex) 
		{
			ex.printStackTrace();
		}
		finally
		{
			if(read != null)
			{
				read.close();
			}
			
			if(out != null)
			{
				out.flush();
				out.close();
			}
		}
	}
    
	private void fileNotExists(HttpServletResponse res) 
	{
		try 
		{
			res.setContentType("text/html; Charset=UTF-8");
			PrintStream out = new PrintStream(res.getOutputStream());
			out.println("<script>alert('파일을 찾을 수 없습니다.');window.close();</script>");
			out.close();
		} 
		catch(Exception ex) 
		{
			log.info(ex.toString());
			log.info(ex.getMessage());
		}
	}    
}

vue에서 호출할 때는 아래와 같이 사용하면 된다.

fileDownLoad(filePath, fileName) {
      const link = `/download/downloadFile.do?filePath=${filePath}&fileName=${encodeURI(fileName)}`;
      window.open(link, '_blank');
},

 

 

[REFERENCE]

 

Leave a Reply

error: Content is protected !!