본문 바로가기
스프링부트

[스프링부트] 입문, 파일 업로드, 파일다운로드 - RESTful , xthhp, RequestParam, FileOutputStream, 입력스트림

by CodeMango 2023. 3. 24.

 

1. Sample1Application.java

public class Sample1Application {

	public static void main(String[] args) {
		SpringApplication.run(Sample1Application.class, args);
	}}

위 코드는 Spring Boot 애플리케이션의 진입점(entry point) 역할을 하는 클래스입니다.

Spring Boot는 자동설정(auto-configuration) 기능을 통해 많은 설정을 자동으로 처리하므로, 애플리케이션의 실행을 위해 별도의 설정 파일이나 코드를 작성할 필요가 없습니다.

대신에, 애플리케이션의 진입점 클래스를 만들고, SpringApplication.run() 메소드를 호출하여 Spring Boot 애플리케이션을 실행합니다.

위 코드에서 SpringApplication.run(Sample1Application.class, args);는 Sample1Application 클래스를 애플리케이션의 진입점으로 지정하고, 애플리케이션을 실행합니다.

이때, args는 애플리케이션 실행 시에 전달할 인자들을 의미합니다.

즉, 위 코드는 Sample1Application 클래스를 애플리케이션의 진입점으로 설정하고,

Spring Boot 애플리케이션을 실행하는 코드입니다.

이를 실행하면 Spring Boot 애플리케이션이 실행되며, 자동설정된 많은 기능을 사용할 수 있습니다.

 

 
스프링부트 실행하기

run as - spring boot app 

 

RESTful 방식

@RestController		// = @Controller + @Responsebody(에이젝스 사용할 때) = @RestController
public class HelloController {

	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String hello() {
		System.out.println("HelloController hello() " + new Date());
		
		return "hello";

위 코드는 Spring Framework를 사용하여 구현된 RESTful 웹 애플리케이션의 컨트롤러입니다.

1) @RestController는 Spring에서 RESTful 웹 애플리케이션에서 사용되는 컨트롤러 클래스에 대한 어노테이션입니다.          @Controller 어노테이션과 @ResponseBody 어노테이션을 함께 사용한 것으로,

    @Controller는 컨트롤러 클래스임을 나타내고,

   @ResponseBody는 해당 메소드가 반환하는 데이터가 HTTP 응답 본문에 포함됨을 나타냅니다.

 

2) @RequestMapping 어노테이션

   해당 메소드가 어떤 URL과 HTTP 요청 메소드와 매핑되는지를 지정합니다.

   위 코드에서는 "/" 경로와 GET 요청 메소드에 매핑되어 있습니다.

 

3) hello() 메소드는 "hello" 문자열을 반환하며, 이 문자열은 HTTP 응답 본문에 포함됩니다.

   또한, 해당 메소드가 호출될 때마다 "HelloController hello() "와 현재 날짜가 출력됩니다.

 

4) 여기서 value 속성은 해당 메소드가 매핑될 URL 패턴을 지정합니다. 

   즉, 클라이언트가 요청할 URL 경로를 지정하는 데 사용됩니다.

   예를 들어, 위 코드에서 @RequestMapping(value = "/")은 클라이언트가 애플리케이션의 루트

   URL(예: http://localhost:8080/)로 GET 요청을 보낼 때, 해당 메소드가 호출되도록 매핑합니다.

   또한, value 속성은 생략 가능하며, 기본값으로 "/"를 사용합니다.

   그러므로 @RequestMapping("/")과 동일한 의미를 가집니다.

 

주의점 : 스프링 프레임워크에서는 controller에서 반환되는 문자열이 View로 간주되어 view로 보내집니다. 

             그러나, 스프링부트에서는 return "hello"; 라는 문자열을 반환하여 HTTP 응답 본문에 포함시켰습니다.

             반환된 문자열은 View 이름으로 간주되는 것이 아니라, 단순히 HTTP 응답 본문으로 전송됩니다.

             따라서, return "hello"; 코드는 "hello"라는 문자열을 HTTP 응답 본문에 포함하여

            클라이언트에게 전송하는 것입니다.
             View를 사용하는 것이 아니기 때문에 ViewResolver를 사용하지 않고, 단순히 문자열 데이터를 반환합니다.

 

sts백엔드 controller

package mul.cam.a.controller;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import mul.cam.a.dto.HumanDto;

@RestController		// = @Controller + @Responsebody(에이젝스 사용할 때) = @RestController
public class HelloController {

	@RequestMapping(value = "/", method = RequestMethod.GET)
	public String hello() {
		System.out.println("HelloController hello() " + new Date());
		
		return "hello";
	}
	/* @RestController는 이렇게 쓴것과 다름 없음
	@Controller		// = @Controller + @Responsebody(에이젝스 사용할 때) = @RestController
	public class HelloController {

		@Responsebody
		@RequestMapping(value = "/", method = RequestMethod.GET)
		public String hello() {
			System.out.println("HelloController hello() " + new Date());
			
			return "hello";
		}	
	*/
	//주소 : localhost:3000/test
	@GetMapping(value = "/test")
	public String test() {
		System.out.println("HelloController test() " + new Date());
		
		return "테스트";
	}
	
	@GetMapping(value = "/human")
	public HumanDto getDto() {
		System.out.println("HelloController getDto()" + new Date());

		HumanDto human = new HumanDto(1001, "홍길동", "서울시");
		
		return human;	//반환 : json형 ("number":1001,"name":"홍길동","address":"서울시"}
						// 보기 편하기 위해 json viewer 다운받으면 더 이쁘게 보임. 
	}
	
	@GetMapping(value = "/conn_param")	//주소 : localhost:3000/conn_param?title=제목입니다
										//->콘솔값title:제목입니다
	public String conn_param(String title) {	//외부에서 들어온것
		System.out.println("HelloController conn_param()" + new Date());
		
		System.out.println("title:"+title);
		
		return "conn success";
	}
	
	@GetMapping(value="param_obj")
	public String param_obj(HumanDto human) {
		System.out.println("HelloController param_obj() " + new Date());
		
		System.out.println(human.toString());
		
		return "OK";
		//localhost:3000/param_obj?number=1002&name=성춘향&address=남원시
		//서버에는 OK  /  콘솔창에는 HumanDto [number=1002, name=성춘향, address=남원시]
	}
	
	//전체 데이터를 list로 던져준다. -> 게시판에서 list던져줄 때 이렇게 한다.
	@GetMapping("/get_list") //사실 value 빼도 된다. 
	public List<HumanDto> get_list(){
		System.out.println("HelloController get_list() " + new Date());
		
		
		List<HumanDto> list = new ArrayList<>();	//먼저 생성하기
		list.add(new HumanDto(101, "홍길동", "서울시"));
		list.add(new HumanDto(102, "성춘향", "남원시"));
		list.add(new HumanDto(103, "임꺽정", "광주시"));
		
		return list;	
		//http://localhost:3000/get_list
		/*	 콘솔에 반환 : HelloController get_list() Fri Mar 24 19:34:16 KST 2023
		 * 
			 서버에 반환 : 						
			 [
			  {
			    "number": 101,
			    "name": "홍길동",
			    "address": "서울시"
			  },
			  {
			    "number": 102,
			    "name": "성춘향",
			    "address": "남원시"
			  },
			  {
			    "number": 103,
			    "name": "임꺽정",
			    "address": "광주시"
			  } */ 
	}
}

 

이클립스 프론트에서 controller를 자바스크립트로 받기

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<!--  비동기통신을 jquery가 아니고 자바스크립트로 만드는 방법 
리액트도 자바스크립트도 만들어야함. -->
<p id= "demo"> </p>

<button type="button" onclick="btnclick()"> hello </button>

<script type="text/javascript">
function btnclick(){
	
	
//xml문법 =자바스크립트용 ajax	
	let xhttp = new XMLHttpRequest();
	
	xhttp.onreadystatechange = function () {
		if(xhttp.readyState == 4 && xhttp.status == 200){ //success(정상 통신 완료)
			document.getElementById("demo").innerText = xhttp.responseText; 
		//responseText로 넘어오는것은 index.html에서서 success :function(str){   의 str임
		//xhttp.responseText 는 object가 아니라 무조건 문자열로 넘어옴
		//jquery는 json으로 받을 수 있었으나, 자바스크립트는 문자열로 ㄴ머어온다.
		
		//문자열로 넘어오니까 json으로 바꿔주기
			let json = JSON.parse(xhttp.responseText);
			//{"number":1001,"name":"홍길동","address":"서울시"} 반환
			
			document.getElementById("demo").innerText = json.name; 
			//name만 
		}
	}
	
	xhttp.open("get", "http://localhost:3000/human", true);
	xhttp.send();
	
}
</script>
</body>
</html>

 

xthhp

XMLHttpRequest(XHR) 객체는 JavaScript에서 서버와 비동기 통신을 위해 사용되는 API 중 하나입니다. 

이 객체를 사용하면 웹 페이지를 새로 고치지 않고도 서버로부터 데이터를 가져올 수 있습니다. 
XMLHttpRequest(XHR) 은 HTTP, HTTPS, FTP 등을 포함한 다양한 프로토콜을 지원하며, 비동기적인 요청을 처리할 수 있기 때문에 페이지의 반응성을 향상시킬 수 있습니다.

위 코드에서 xhttp는 XMLHttpRequest 객체를 생성한 변수 이름입니다. 이 객체를 사용하여 GET 방식으로 서버에 요청을 보내고, 서버로부터 받은 응답을 처리하는 함수를 정의하고 있습니다. 즉, xhttp는 서버와의 비동기적인 통신을 담당하며, 클라이언트 측 JavaScript 코드에서 서버로부터 데이터를 가져오기 위해 사용됩니다.

 


파일업로드 개념정리

백엔드

HelloController.java

	// upload
	@RequestMapping(value = "/fileUpload", method = RequestMethod.POST)
	public String fileUpload(HumanDto human, 
							@RequestParam("uploadFile")MultipartFile uploadFile, 
							HttpServletRequest req) {
		System.out.println("HelloController fileUpload " + new Date());
		System.out.println(human.toString());
		
		// 경로
		String path = req.getServletContext().getRealPath("/upload");	
        //서버(톰캣)에 올리는것-> 실제 서비스할 때 
		//src-main-webapp-upload 안에 있음.
        
		// String path = "c:\temp";	
        //클라이언트에 올리는것 (없어지지 않는다) ->공부할 때(연습용)
		
		String filename = uploadFile.getOriginalFilename();				
		String filepath = path + "/" + filename;
		
		System.out.println(filepath);
	
    	File file = new File(filepath);
		
		try {
			BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
			bos.write(uploadFile.getBytes());			
			bos.close();
			
		} catch (Exception e) {			
			return "file upload fail";
		} 
		
		return "file upload success";
	}

 

위의 코드는 파일 업로드 기능을 구현한 Spring Controller 메서드입니다.

파일 업로드 기능은 보통 HTTP POST 방식으로 구현됩니다. 이는 파일이 큰 용량을 차지하고, 클라이언트가 서버로 파일을 보내는 과정에서 바이너리 데이터를 전송하기 때문입니다. HTTP GET 방식으로는 바이너리 데이터를 전송하기에는 적합하지 않습니다.

바이너리데이터

바이너리 데이터(binary data)는 0과 1로 이루어진 이진(binary) 형식의 데이터를 말합니다. 이진 형식은 일반적으로 텍스트 형식과 달리, 보통의 문자열 인코딩 방식으로는 처리할 수 없는 이미지, 음악, 동영상 등의 멀티미디어 데이터를 나타내는 데 사용됩니다.

 

POST vs GET 

HTTP GET과 POST는 웹 브라우저에서 서버로 요청을 보내는 방식 중 가장 대표적인 두 가지 방식입니다.

 

HTTP GET은 서버로부터 정보를 받아오기 위한 요청 방식으로, URL에 파라미터를 포함하여 전송합니다.

따라서 URL에 모든 정보가 노출되기 때문에, 보안에 취약합니다.

또한 URL 길이가 제한될 수 있어, 대용량 데이터나 바이너리 데이터 전송에는 적합하지 않습니다.


HTTP POST는 서버로 데이터를 보내기 위한 요청 방식으로, HTTP 헤더와 바디를 사용하여 데이터를 전송합니다.

따라서 GET 방식보다 보안에 강하고, 대용량 데이터나 바이너리 데이터를 전송할 수 있습니다.

 

@RequestParam(요청파라미터 받아오기)

Spring MVC에서 HTTP 요청 파라미터를 메서드 파라미터에 바인딩하는 데 사용되는 어노테이션입니다.

HTTP 요청에서 전달된 파라미터를 컨트롤러의 메서드 파라미터로 바인딩하기 위해 사용됩니다.

@RequestParam 어노테이션을 사용하면, 요청 URL에서 파라미터 값을 추출하여 해당 메서드 파라미터에 자동으로 매핑할 수 있습니다.

예를 들어, 아래와 같은 URL이 있다고 가정해봅시다.

http://example.com/hello?name=John&age=30

이 URL에서 name과 age는 HTTP 요청 파라미터입니다. 

이 파라미터를 Spring 컨트롤러에서 사용하려면, 다음과 같이 @RequestParam 어노테이션을 메서드 파라미터에 적용합니다.

@GetMapping("/hello")
public String hello(@RequestParam("name") String name, @RequestParam("age") int age) {
    // 파라미터로 전달된 값 사용
    return "Hello, " + name + "! You are " + age + " years old.";
}

위 코드에서 @RequestParam 어노테이션을 사용하여, HTTP 요청에서 name과 age 파라미터 값을 추출하여 String과 int 타입의 메서드 파라미터에 바인딩합니다. name과 age는 URL에서 추출되어 해당 메서드의 파라미터에 자동으로 매핑됩니다.

 

백엔드 - 파일 업로드 코드분석

HelloController.java

	// upload
	@RequestMapping(value = "/fileUpload", method = RequestMethod.POST)
	public String fileUpload(HumanDto human, 
							@RequestParam("uploadFile")MultipartFile uploadFile, 
							HttpServletRequest req) {
		System.out.println("HelloController fileUpload " + new Date());
		System.out.println(human.toString());
		
		// 경로
		String path = req.getServletContext().getRealPath("/upload");	
        //서버(톰캣)에 올리는것-> 실제 서비스할 때 
		//src-main-webapp-upload 안에 있음.
        
		// String path = "c:\temp";	
        //클라이언트에 올리는것 (없어지지 않는다) ->공부할 때(연습용)
		
		String filename = uploadFile.getOriginalFilename();				
		String filepath = path + "/" + filename;
		
		System.out.println(filepath);
	
    	File file = new File(filepath);
		
		try {
			BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));
			bos.write(uploadFile.getBytes());			
			bos.close();
			
		} catch (Exception e) {			
			return "file upload fail";
		} 
		
		return "file upload success";
	}

 

코드 전체 해석

 

@RequestMapping(value = "/fileUpload", method = RequestMethod.POST) :

  HTTP POST 요청을 처리하는 메서드임을 나타내며, "/fileUpload" 경로에서 요청을 처리합니다.

@RequestParam("uploadFile") MultipartFile uploadFile :

  HTTP 요청에서 uploadFile이라는 이름으로 전달된 Multipart 데이터를, MultipartFile 타입의 uploadFile이라는 메서드 파라미터에 바인딩하기 위해 사용되는 어노테이션입니다.

String path = req.getServletContext().getRealPath("/upload") :

파일을 저장할 디렉토리 경로를 지정합니다.getServletContext().getRealPath() 메서드를 사용하여 현재 서블릿 컨텍스트의 실제 경로를 가져온 후, /upload 경로를 추가하여 파일을 저장할 경로를 생성합니다.

String filename = uploadFile.getOriginalFilename() :

업로드된 파일의 원본 파일 이름을 가져옵니다.

String filepath = path + "/" + filename :

디렉토리 경로와 파일 이름을 결합하여 파일 경로를 생성합니다.

File file = new File(filepath) :

파일 경로에 해당하는 File 객체를 생성합니다.

try-catch 블록 내부에서 BufferedOutputStream을 이용하여 파일 출력 스트림을 생성한 후,

write() 메서드를 사용하여 MultipartFile에서 가져온 파일 데이터를 파일에 저장합니다.

파일 출력 작업이 완료되면 출력 스트림을 닫습니다.

마지막으로, 파일 업로드가 성공하면 "file upload success"를 반환하고,

파일 업로드에 실패하면 "file upload fail"를 반환합니다.

 

코드 개별 해석

HttpServletRequest req

HttpServletRequest는 Servlet API에서 제공하는 인터페이스 중 하나입니다. 

이 인터페이스는 HTTP 요청 정보를 담고 있는 객체로, 

클라이언트로부터 전송된 HTTP 요청 정보를 추출하는 메서드를 제공합니다.

 

HttpServletRequest 객체는 Spring MVC에서 컨트롤러 메서드의 파라미터로 선언하여 사용할 수 있습니다.

이를 이용하여 클라이언트에서 전송된 HTTP 요청 정보를 추출하거나,

HTTP 세션 객체에 접근하는 등의 작업을 수행할 수 있습니다.

 

위 코드에서 HttpServletRequest req는 HTTP 요청 정보를 담고 있는 HttpServletRequest 객체입니다. req.getServletContext().getRealPath() 메서드를 사용하여 현재 서블릿 컨텍스트의 실제 경로를 가져올 수 있습니다.

이를 이용하여 파일을 저장할 경로를 생성합니다.

 

따라서 req.getServletContext().getRealPath("/upload"); 라는 코드를 쓰기 위해 HttpServletRequest를 사용했다고 볼 수 있습니다.

 

BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(filepath)));

위의 코드는 파일 경로(filepath)에 해당하는 파일에 대한 FileOutputStream을 생성하고,

이를 BufferedOutputStream으로 래핑하는 코드입니다.

new File(filepath) 는 파일 경로(filepath)를 이용하여 java.io.File 객체를 생성합니다.

이 객체는 파일이나 디렉토리에 대한 경로 정보를 제공하고, 파일 또는 디렉토리를 생성하거나 삭제하는 등의 작업을 수행할 수 있는 기능을 제공합니다.

 

new FileOutputStream(new File(filepath)) 는 File 객체를 이용하여 파일에 대한 FileOutputStream을 생성합니다.

이 FileOutputStream은 파일에 대한 출력 스트림을 제공합니다.

 

new BufferedOutputStream(new FileOutputStream(new File(filepath))) 는 앞서 생성한

FileOutputStream 객체를 BufferedOutputStream으로 래핑하여, 파일에 대한 출력 작업을 수행합니다. BufferedOutputStream은 입출력 성능을 향상시키기 위해 출력 데이터를 내부 버퍼에 쌓아두었다가, 일정 크기 이상의 데이터가 쌓이면 한 번에 출력하는 방식으로 동작합니다.

 

따라서 new 키워드가 많이 사용된 것은, 객체 생성 과정에서 필요한 객체를 연속해서 생성하기 위한 것입니다. 먼저 파일 경로를 이용하여 File 객체를 생성하고, 이를 이용하여 파일에 대한 FileOutputStream 객체를 생성한 뒤, 이 객체를 BufferedOutputStream으로 래핑합니다.

 

FileOutputStream

FileOutputStream은 자바에서 파일에 데이터를 바이트 단위로 쓰기 위한 OutputStream의 구현 클래스 중 하나입니다.

OutputStream은 자바에서 출력 스트림을 추상화한 클래스로, 바이트 단위로 데이터를 출력하는 기능을 제공합니다. FileOutputStream은 OutputStream을 상속받아 파일에 대한 출력 스트림을 제공하는 클래스입니다.

FileOutputStream을 이용하여 파일에 데이터를 쓰기 위해서는 먼저 파일에 대한 경로를 지정하고,

FileOutputStream 객체를 생성합니다. 그리고 write() 메서드를 호출하여 파일에 데이터를 씁니다.

아래는 FileOutputStream를 이용하여 파일에 데이터를 쓰는 간단한 예시입니다.

try (FileOutputStream fos = new FileOutputStream("data.txt")) {
    fos.write("Hello, world!".getBytes());
} catch (IOException e) {
    e.printStackTrace();
}

위 코드는 "data.txt" 파일에 "Hello, world!" 문자열을 쓰는 예시입니다.

FileOutputStream 객체를 생성할 때 파일 경로를 인자로 전달하고, write() 메서드를 호출하여 데이터를 파일에 씁니다.

예외 처리를 위해 try-catch 문을 사용하여 파일 출력 작업에서 발생할 수 있는 예외를 처리합니다.

 

프론트엔드

index.html

<h3>file upload</h3>

<p id="fileResult">...</p>

<form id="uploadFileForm">
	<!-- 서버에 이미 value값이 입력되어있음 -->
	number:<input type="text" name="number" value="1002"><br>	
	name:<input type="text" name="name" value="성춘향"><br>
	address:<input type="text" name="address" value="남원시"><br><br>	
	
	<!--  type을 file로 지정하면 파일선택 버튼 생성됨 -->
	file:<input type="file" name="uploadFile"><br><br>	
	<!-- 백엔드의 @RequestParam("uploadFile") 값과 매칭시켜야함 -->
	
	<button type="button" id="uploadBtn">파일업로드</button>	
</form>

<script type="text/javascript">
$(document).ready(function(){
	$("#uploadBtn").click(function(){
		
		$.ajax({
			url:"http://localhost:3000/fileUpload",	//controller의 @RequestMapping(value = "/fileDownload")
			type:"post",	//업로드는 post
			data:new FormData($("#uploadFileForm")[0]), //FormDate JS클래스
			enctype:'multipart/form-data',	//인코딩타입 form으로 넘기므로
			processData:false,	//처리용데이타
			contentType:false,	//
			cache:false,
			success:function(str){
				alert('success');	//
				$("#fileResult").text(str);
			},
			error:function(){
				alert('error');
//저장폴더-C:\Users\Hana\Desktop\풀스택교육 자료들\백엔드\스프링\0302 스프링부트\백엔드\sample2-file\src\main\webapp\upload 
		}
		});		
	});
});
</script>

 


파일 다운로드 개념정리

MediaTypeUtiles.java (백앤드 유틸)

    package mul.cam.a;

import javax.servlet.ServletContext;

import org.springframework.http.MediaType;
//download를 위한 파일

public class MediaTypeUtiles {
	
	public static MediaType getMediaTypeForFileName(ServletContext servletContext, String filename) {
		
		String mimType = servletContext.getMimeType(filename);
		
		try {
			MediaType mediaType = MediaType.parseMediaType(mimType);			
			return mediaType;	//결국 mimetype이 반환됨.
		}catch (Exception e) {
			return MediaType.APPLICATION_OCTET_STREAM;
			//APPLICATION_OCTET_STREAM : 모든 바이너리 데이터를 나타내는 미디어 타입
		}		
	}
}

MediaTypeUtils 클래스 : 파일 이름을 기반으로 해당 파일의 미디어 타입(MediaType)을 반환하는 유틸리티 클래스입니다.

getMediaTypeForFileName 메소드 :  ServletContext 객체와 파일 이름(filename)을 매개변수로 받습니다.

ServletContext           :  서블릿 컨테이너에서 제공하는 인터페이스로, 웹 애플리케이션의 정보를 제공합니다. 

 

getMimeType 메소드 : ServletContext에서 제공하는 getMimeType 메소드를 사용하여 파일 이름(filename)을 기반으로

                                      해당 파일의 MIME 타입을 가져옵니다. 


MediaType 클래스의 parseMediaType 메소드를 사용하여 mimeType 문자열을 MediaType 객체로 파싱합니다.

MediaType 객체는 미디어 타입을 나타내는 클래스로, HTTP 요청 및 응답에서 사용됩니다.

만약 mimeType 문자열을 파싱하는 도중 예외가 발생하면, MediaType.APPLICATION_OCTET_STREAM을 반환합니다. 

APPLICATION_OCTET_STREAM은 모든 바이너리 데이터를 나타내는 미디어 타입입니다.

 

즉, getMediaTypeForFileName 메소드는 파일 이름을 기반으로 해당 파일의 미디어 타입을 반환하는 메소드이며,

ServletContext를 사용하여 파일의 MIME 타입을 가져오고,

MediaType 클래스를 사용하여 MIME 타입을 MediaType 객체로 파싱하여 반환합니다.

미디어타입은 MIME타입이라고도 합니다. 예를 들어 HTML문서의 MIME타입은 test/html 입니다.

 

 


HelloController.java (백앤드)

	@Autowired
	ServletContext servletContext; 

	
	@RequestMapping(value = "/fileDownload")
										//filename : 클라이언트에서 다운로드할 파일의 이름을 나타냄(upload와 별개)
	public ResponseEntity<InputStreamResource> download(String filename, HttpServletRequest req) throws Exception {
		System.out.println("HelloController download " + new Date());
		
		// 경로
		String path = req.getServletContext().getRealPath("/upload"); // //서버(톰캣)에 올리는것-> 실제 서비스할 때  
		// String path = "c:\temp"; 클라이언트에 올리는것 (없어지지 않는다) -> 공부할 때(연습용)
		
							//MediaTypeUtiles.java의 함수를 불러옴
		MediaType mediaType = MediaTypeUtiles.getMediaTypeForFileName(this.servletContext, filename);
														//이(this) 화면에서 @Autowired로 선언한 servletContext 에 접근
		System.out.println("filename:" + filename);
		System.out.println("mediaType:" + mediaType);
		
		File file = new File(path + File.separator + filename);		// newfilename
		
		InputStreamResource isr = new InputStreamResource(new FileInputStream(file));
		
		// db 다운로드 카운트
		
		return ResponseEntity.ok()
					.header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + file.getName()) // 원본파일명
					.contentType(mediaType)
					.contentLength(file.length())
					.body(isr);					
	}
}

 

코드분석

@Autowired
	ServletContext servletContext;

@Autowired 어노테이션을 사용하면 Spring Framework이 ServletContext 객체를 찾아서 해당 클래스의 멤버 변수에 주입해주게 됩니다.

이렇게 함으로써 개발자는 ServletContext 객체를 따로 생성하거나 관리할 필요 없이 자유롭게 사용할 수 있게 됩니다.

controller 전체 코드에서

MediaType mediaType = MediaTypeUtiles.getMediaTypeForFileName(this.servletContext, filename);

코드가 ServletContext 객체를 사용하고 있습니다.

MediaTypeUtiles 클래스에서 getMediaTypeForFileName 메소드를 호출할 때 this.servletContext를 인자로 전달하여 사용합니다. getMediaTypeForFileName 메소드에서는 servletContext 객체를 사용하여 MIME 타입을 가져옵니다.

 

전체 코드 분석

 

  1. @RequestMapping(value = "/fileDownload") : URL 매핑을 설정하는 어노테이션입니다. "/fileDownload"로 들어오는 요청이 이 메소드에서 처리됩니다.
  2. public ResponseEntity<InputStreamResource> download(String filename, HttpServletRequest req) throws Exception { :   파일 다운로드를 처리하는 메서드이며, ResponseEntity 객체를 반환합니다. 이 메서드는 다운로드할 파일 이름과 HTTPServletRequest 객체를 매개변수로 받고, Exception을 던질 수 있습니다. 요청이 들어오면, filename과 HttpServletRequest를 매개변수로 받아옵니다. ResponseEntity<InputStreamResource>는 다운로드할 파일을 브라우저로 전송할 때 필요한 객체입니다.
  3. System.out.println("HelloController download " + new Date()); : 다운로드 요청이 들어왔음을 콘솔에 출력합니다.
  4. String path = req.getServletContext().getRealPath("/upload"); : 파일 경로를 지정합니다. getRealPath는 상대경로를 절대경로로 변환해줍니다. (실제경로)
  5. MediaType mediaType = MediaTypeUtiles.getMediaTypeForFileName(this.servletContext, filename); : 파일의 확장자에 따라 다운로드할 파일의 MIME 타입을 결정합니다. ==  파일 이름을 기반으로 MediaType을 결정하는 유틸리티 클래스의 메서드를 사용하여 MediaType을 가져옵니다.
  6. System.out.println("filename:" + filename): 콘솔에 다운로드할 파일 이름을 출력합니다.
  7. System.out.println("mediaType:" + mediaType): 콘솔에 가져온 MediaType을 출력합니다.
  8. File file = new File(path + File.separator + filename); :다운로드할 파일의 경로와 이름을 기반으로 File 객체를 생성합니다.
  9. InputStreamResource isr = new *InputStreamResource(new FileInputStream(file)); : 다운로드할 파일의 *입력 스트림을 생성합니다. ==다운로드할 파일의 InputStream을 가져와서 InputStreamResource 객체를 생성합니다.
  10. return ResponseEntity.ok()...: 다운로드할 파일의 InputStreamResource 객체와 함께 HTTP 응답을 반환합니다.        이 응답에는 다운로드할 파일의 이름, 크기, MediaType이 포함됩니다.
  11. header(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=" + file.getName()) :                                    HTTP 응답 헤더에 다운로드할 파일의 정보를 추가합니다.                                                                                "attachment"는 다운로드할 파일의 형식을 지정하는 값으로, 브라우저가 이를 인식하여 다운로드를 실행합니다. "filename"은 다운로드할 파일의 이름을 나타냅니다.  > 예를 들어, file 변수가 C:/example/file.zip 파일을 가리키고 있고, 이 파일의 이름이 file.zip이라면, file.getName()은 file.zip 문자열을 반환합니다. 

HttpHeaders.CONTENT_DISPOSITION는 HTTP 응답 헤더의 하나로, 해당 응답이 다운로드될 수 있는 파일임을 브라우저에게 알리는 역할을 합니다. "attachment" 값은 해당 파일을 다운로드하기 위해 브라우저에서 파일 다운로드 창이 열리도록 하는 특별한 지시어입니다. 그리고 그 뒤에 ";filename="과 함께 파일 이름을 지정해주어, 다운로드되는 파일의 이름을 지정할 수 있습니다. 이 헤더는 다운로드 링크를 생성하는 데 매우 유용하며, 파일 다운로드 기능을 구현하는 데 필수적인 요소 중 하나입니다.

 

  12. contentType(mediaType): HTTP 응답 헤더에 다운로드할 파일의 MIME 타입을 지정합니다.

  13. contentLength(file.length()): HTTP 응답 헤더에 다운로드할 파일의 크기를 지정합니다.

  14. body(isr): HTTP 응답 본문에 다운로드할 파일의 내용을 전달합니다. InputStreamResource 객체인 isr은

        파일의 입력 스트림을 포함하고 있습니다.

 

입력스트림

입력스트림(Input Stream)은 프로그램에서 데이터를 입력받을 때 사용되는 스트림(stream)입니다. 데이터 소스로부터 데이터를 읽어오는 데 사용됩니다.

예를 들어, 사용자가 키보드를 통해 문자를 입력하면, 프로그램은 입력스트림을 통해 문자 데이터를 읽어올 수 있습니다. 또한 파일에서 데이터를 읽어오기 위해서도 입력스트림을 사용할 수 있습니다.

입력스트림은 일련의 바이트(byte)들을 읽어와서 문자, 숫자, 객체 등의 형태로 변환할 수 있습니다. 대표적인 입력스트림의 예로는 Java에서 InputStream, C++에서 std::istream 등이 있습니다.

 

InputStreamResource

InputStreamResource는 Spring Framework에서 제공하는 클래스 중 하나로, 자바의 InputStream을 Spring의 Resource 인터페이스와 함께 사용하기 위한 클래스입니다.

InputStreamResource는 InputStream을 생성자의 매개변수로 받아서 *Resource 인터페이스를 구현한 객체를 반환합니다. 이 객체는 InputStream으로부터 읽어들인 데이터를 읽을 수 있는 메소드들을 제공합니다.

따라서, InputStreamResource는 입력스트림(InputStream)을 이용하여 데이터를 읽어오기 위한 용도로 사용됩니다. Spring Framework에서는 이 클래스를 통해 다양한 데이터를 읽어들일 수 있는 방법을 제공하고 있습니다. 예를 들면, 파일, 네트워크, 데이터베이스 등에서 데이터를 읽어들이는 것이 가능합니다.

 

Resource 인터페이스

Resource 인터페이스는 Spring Framework에서 파일 시스템, 클래스 경로, URL, 서블릿 컨텍스트 등 다양한 위치에 있는 리소스를 추상화하여 다룰 수 있도록 도와주는 인터페이스입니다.

Resource 인터페이스를 통해 리소스를 다룰 때, 파일 시스템에 존재하는 파일이든, JAR 파일 안에 포함되어 있는 파일이든, URL로 접근할 수 있는 파일이든, 어떤 종류의 리소스든 일관된 방식으로 다룰 수 있습니다. 이는 개발자가 리소스의 위치와 타입에 대한 세부적인 정보를 몰라도 편리하게 리소스를 다룰 수 있도록 도와줍니다.

 

Resource 인터페이스를 통해 다양한 위치의 리소스를 추상화하는 이유는, 개발자가 리소스의 위치와 타입에 대한 세부적인 정보를 몰라도 일관된 방식으로 리소스를 다룰 수 있도록 하기 위함입니다.

예를 들어, 개발자가 파일 시스템에 저장된 파일을 다루는 코드를 작성할 때, 파일의 경로와 타입에 대한 정보를 알고 있어야 합니다. 하지만 Resource 인터페이스를 사용하면 파일 시스템에 저장된 파일 뿐만 아니라, JAR 파일 안에 포함되어 있는 파일이나 URL로 접근 가능한 파일 등 다양한 리소스를 일관된 방식으로 다룰 수 있습니다. 이는 개발자가 리소스의 위치와 타입에 대한 세부적인 정보를 몰라도 편리하게 리소스를 다룰 수 있도록 도와줍니다.

또한, 리소스를 추상화함으로써 Spring Framework는 응용 프로그램의 유연성과 확장성을 높일 수 있습니다. 만약 리소스의 위치나 타입이 변경되더라도, Resource 인터페이스를 구현한 클래스를 수정함으로써 응용 프로그램을 수정할 필요 없이, 기존 코드를 그대로 사용할 수 있습니다. 이는 유지 보수성과 개발 생산성을 높일 수 있는 장점을 가지고 있습니다.

 

ResponseEntity.ok()

ResponseEntity.ok()는 Spring Framework에서 제공하는 ResponseEntity 클래스의 정적 메소드 중 하나입니다.
이 메소드는 HTTP 요청에 대한 응답을 생성하는 데 사용됩니다. ok() 메소드는 HTTP 상태 코드 200(OK)을 반환하며, 응답 본문은 비어있습니다.
이후에 체인 메소드인 .body()나 .headers() 등을 이용하여 응답 본문과 헤더를 설정할 수 있습니다.

따라서, ResponseEntity.ok()는 200(OK) 상태 코드를 반환하는 HTTP 응답 객체를 생성하고, 이를 응답으로 반환하는 데 사용됩니다. 이 응답 객체는 클라이언트로부터의 HTTP 요청에 대한 성공적인 처리를 나타내는 응답 메시지를 생성하기 위해 사용됩니다.

index.html(프론트앤드)

<h3>file download</h3>

<button type="button" id="downloadBtn">파일 다운로드</button>

<script type="text/javascript">

$("#downloadBtn").click(function(){
	
	//location.href로 다운로드 할 수 있음. 
	location.href = "http://localhost:3000/fileDownload?filename=" + "zoom.txt";
	
	/* ajax 로 보내면 다운로드 안됨.
	$.ajax({
		url:"http://localhost:3000/fileDownload",
		type:"get",
		data:{ filename:"zoom.txt" },
		success:function(){
			alert('success');	
		},
		error:function(){
			alert('error');
		}		
	});
	*/
});

</script>

 

 

 

서버 출력

댓글