본문 바로가기
Programming/Spring Boot & Vue.js

Spring Boot & Vue.js - Aixos 파일 다운로드

by 주리니e 2023. 6. 13.
728x90

Spring Boot & Vue.js - Aixos 파일 다운로드 (File Download)

 

 

클라이언트(Vue)에서는 axios 를 이용해 서버로 파일 다운로드를 요청하고 서버(Spring Boot) 에서는 클라이언트로부터 요청을 받아 파일 다운로드를 처리하는 기능을 구현해보고자 한다. Spring Boot와 Vue를 연동하는 방법은 아래 링크를 이용하여 구축하면 된다.

I would like to implement a feature where the client (Vue) requests file download from the server using axios, and the server (Spring Boot) handles the request and initiates the file download to the client. You can set up the integration between Spring Boot and Vue using the link below.

 

Spring Boot & Vue.js 연동 및 개발환경 구축

Spring Boot & Vue.js 연동 및 개발환경 구축 개발환경 Eclipse IDE Visual Studio Code Spring Boot 2.7.5 Vue@3.2.41 Gradle 7.0 여태까지 Spring에서 권고하는 방식인 Thymeleaf + HTML 기반으로 프론트엔드단을 개발하였는데

jiurinie.tistory.com

 

 

  • 다운로드 샘플파일 (Download Sample File)

다운로드를 할 수 있도록 바탕화면에 샘플파일을 생성한다.

To enable downloading, create a sample file on the desktop.

 

 

 

Server (Spring Boot)

  • FileController.java

클라이언트로부터의 요청을 받아 ResponseEntity객체를 생성하여 다운로드할 파일의 정볼르 설정하고 해당 파일의 리소스를 바디로 설정한다. 파일명은 UTF-8로 인코딩하여 헤더에 포함시키고, HttpHeaders.CONTENT_DISPOSITION 헤더에 "attachment" 값을 설정하여 첨부파일로 다운로드하도록 지정한다. 파일명을 UTF-8로 인코딩하는 이유는 파일명에 사용되는 문자열이 다양한 언어 또는 특수문자나 공백 등의 문자를 포함할 수 있기 때문이다.

Receive the request from the client, create a ResponseEntity object, set the information of the file to be downloaded, and set the file resource as the body. Encode the filename in UTF-8 and include it in the header, and set the HttpHeaders.CONTENT_DISPOSITION header to "attachment" to specify that it should be downloaded as an attachment. The reason for encoding the filename in UTF-8 is that the filename may contain various languages or special characters, including special characters or spaces.

import java.io.IOException;
import java.net.URLEncoder;

import javax.servlet.http.HttpServletRequest;

import org.springframework.core.io.Resource;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;

import kr.or.kipro.cls.service.FileService;
import lombok.AllArgsConstructor;

@Controller
@AllArgsConstructor
public class FileController {

	private final FileService fileService;

	@PostMapping("/api/file/download")
	public ResponseEntity<Resource> download(HttpServletRequest request, @RequestParam String filename) {

		Resource resource = fileService.loadFileAsResource(filename);

		String contentType = null;

		try {
			filename = URLEncoder.encode(filename, "UTF-8").replace("+", "%20");
			contentType = request.getServletContext().getMimeType(resource.getFile().getAbsolutePath());
		} catch (IOException ex) {
		}

		if (contentType == null) {
			contentType = "application/octet-stream";
		}

		return ResponseEntity.ok().contentType(MediaType.parseMediaType(contentType)).header(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename*=UTF-8''" + filename).body(resource);
	}
}

 

 

 

  • FileService.java

파일이 존재하지 않는다면, MyFileNotFoundException 예외를 던지고, 파일이 존재하면 해당 경로를 찾아 파일의 리소스를 제공한다.

If the file does not exist, throw a MyFileNotFoundException exception. If the file exists, find the corresponding path and provide the file resource.

import java.nio.file.Path;
import java.nio.file.Paths;

import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;

import kr.or.kipro.cls.exception.MyFileNotFoundException;

@Service
public class FileService {
	public Resource loadFileAsResource(String fileName) {
		try {
			Path targetLocation = Paths.get("C:\\Users\\user\\Desktop".toString()).resolve(fileName);
			Resource resource = new UrlResource(targetLocation.toUri());

			if (resource.exists()) {
				return resource;
			} else {
				throw new MyFileNotFoundException("File not found " + fileName);
			}
		} catch (Exception ex) {
			throw new MyFileNotFoundException("File not found " + fileName, ex);
		}
	}
}

 

 

 

  • MyFileNotFoundException.java

파일을 찾을 수 없는 경우 해당 예외를 발생시킨다. 예외는 HttpStatus.NOT_FOUND 상태 코드를 반환하도록 설정한다.

If the file cannot be found, raise the corresponding exception. Set the exception to return the HttpStatus.NOT_FOUND status code.

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(HttpStatus.NOT_FOUND)
public class MyFileNotFoundException extends RuntimeException {

	private static final long serialVersionUID = 6034167892600448190L;

	public MyFileNotFoundException(String message) {
		super(message);
	}

	public MyFileNotFoundException(String message, Throwable cause) {
		super(message, cause);
	}
}

 

 

Client (Vue)

  • Axios Setup
> npm install axios

 

 

  •  App.vue

File Download 링크를 클릭하면 axios.post를 사용하여 서버에 파일 다운로드를 요청하고 응답받은 헤더로부터 'content-disposition' 값을 가져와서 파일명을 추출한다. 이때, 서버로부터 받은 파일명은 UTF-8로 인코딩되어 있으므로 디코딩한다. 그 후 a 태그를 동적으로 생성하여 window.URL.createObjectURL의 url 을 href 속성에 설정한다. 링크를 body에 추가하고 link.click메소드를 호출하여 파일을 다운로드 한다.

When the File Download link is clicked, the client uses axios.post to send a file download request to the server. The response headers are then used to extract the 'content-disposition' value, from which the filename is extracted. Since the filename received from the server is encoded in UTF-8, it is decoded. Afterwards, an <a> tag is dynamically created, and the URL generated by window.URL.createObjectURL is set as the href attribute of the link. The link is added to the body, and the link.click() method is called to initiate the file download.

<template>
  <img alt="Vue logo" src="./assets/logo.png">
  <h3><a @click="download('새 텍스트 문서.txt')">File Download</a></h3>
</template>

<script>
import axios from "axios";

export default {
  name: 'App',
  components: {
  },
  methods: {
    download(filename){
      axios.post('/api/file/download', null, { params :{
						filename: filename,
					}}
				)
				.then(result => {
					const disposition = result.headers['content-disposition'];
					const encodedFilename = disposition.split("UTF-8''")[1];
					const filename = decodeURIComponent(encodedFilename);
					const url = window.URL.createObjectURL(new Blob([result.data]));
					const link = document.createElement('a');
					link.href = url;
					link.setAttribute('download', filename);
					document.body.appendChild(link);
					link.click();
				})
				.catch(error => {
					if (error.response && error.response.status === 404) {
						console.log('File Not Found.');
					}
				});
    }
  }
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

728x90

댓글