Java

2024.08.01 JSP 프로그래밍 기본 JSP 파일 업로드

정훈5 2024. 8. 1. 12:02

 

학습 목표 
외부 라이브러리 없이 서블릿이 제공하는 자바 스펙으로 파일 업로드를 구현해 봅시다. 

1.  HTTP 메세지에서 이진 데이터와 텍스트 형태를 알고 있는가?
2. multipart/form-data  MIME TYPE에 대해서 알아 보자. 
3. 스트림을 알고 있는가?

 

1. HTTP 메세지에서 이진 데이터와 텍스트 형태를 알고 있는가?

텍스트 기반 (MIME TYPE - text/plain )

POST /example HTTP/1.1
Host: example.com
Content-Type: text/plain
Content-Length: 13
---- CLRF 빈줄 공백 --------
Hello

 

 

이진데이터 (MIME TYPE - application/octet-stream )

POST /example HTTP/1.1
Host: example.com
Content-Type: application/octet-stream
Content-Length: 5
---- CLRF 빈줄 공백 --------
01001000 01100101 01101100 01101100 01101111

 

application/octet-stream 타입으로 지정된 경우, 본문 데이터는 그대로 전송 됩니다.

이 MIME 타입은 바이너리 데이터를 나타내며, 서버가 이를 특별한 인코딩이나 디코딩 없이 원시 데이터로 취급하게 합니다.

 

2. multipart/form-data MIME TYPE에 대해서 알아 보자.

multipart/form-data는 주로 웹 애플리케이션에서 파일 업로드와 같은 복합적인 데이터 전송에 사용되는 형식입니다.

텍스트 데이터와 바이너리 데이터

  • 텍스트 데이터: 텍스트 데이터는 일반적으로 문자로 이루어져 있으며, 전송 과정에서 특별한 변환 없이 텍스트 형식 그대로 전송됩니다. 텍스트 필드의 내용은 UTF-8과 같은 문자 인코딩을 통해 바이트 시퀀스로 변환되어 전송됩니다.

  • 바이너리 데이터: 바이너리 데이터는 이미지, 오디오 파일, 비디오 파일 등과 같은 파일 형식으로 주로 나타납니다. 이 데이터는 원래의 바이너리 형식 그대로 전송됩니다.

multipart/form-data 형식의 특징은 데이터를 여러 부분(파트)으로 나누어 전송할 수 있다는 점입니다. 각 파트는 자체적으로 여러개의 (헤더와 바디)를 가지고 있으며, 이 파트들이 조합되어 전체 요청 본문을 구성합니다.

 

POST /upload HTTP/1.1
Host: example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="textField"

This is a text.
----WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="example.txt"
Content-Type: text/plain

(File content goes here)
----WebKitFormBoundary7MA4YWxkTrZu0gW--

 

  • Boundary (경계 문자열): 각 파트는 고유한 경계 문자열로 구분됩니다. 이 문자열은 요청 본문의 시작과 끝을 나타냅니다.
  • Part Header (부분 헤더): 각 파트는 데이터에 대한 설명을 담고 있는 헤더를 포함할 수 있습니다. 예를 들어, 파일의 이름, 필드 이름, 데이터의 유형(Content-Type) 등을 포함할 수 있습니다.
  • Part Body (부분 본문): 여기에는 실제 데이터가 들어갑니다. 예를 들어, 텍스트 필드의 내용이나 업로드된 파일의 내용이 포함됩니다.

 

 

jsp_file_upload_ex1

index.jsp

더보기

index.jsp

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<div class="main--container">
	<!-- 파일은 post 방식으로 던진다. -->
	<!-- 파일을 전송하기 위한 설정 -->
		<form action="/upload" method="post" enctype="multipart/form-data">
			<label for="title">제목 : </label>
			<input type="text" name="title" id="title">
			
			<label for="mFile">첨부 파일 : </label>
			<input type="file" name ="mFile" id ="mFile">
			
			<button type="submit">전송</button>
		</form>
		
	</div>

</body>
</html>

FileUploadController.java

더보기

FileUploadController.java

package com.tenco.controller;

import java.io.File;
import java.io.IOException;
import java.util.UUID;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.MultipartConfig;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.Part;

/**
 * 서블릿 스펙에서 파일 처리를 할거면
 * 반드시 어노테이션 하나가 더 필요하다.
 * @MultipartConfig 이녀석이 필요하다.
 */

@WebServlet("/upload")
@MultipartConfig // 반드시 선언
public class FileUploadController extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    public FileUploadController() {
    }
    
    

    // POST 방식만 사용하기 때문에 GET 삭제 했음
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		System.out.println("11111111111");
		// 'mFile' 이라는 key 값으로 input 태그로 파일 데이터를 가져올 수 있다.
		// 파일은 getPart 을 활용한다.
		Part filePart = request.getPart("mFile");
		System.out.println(filePart.getContentType());
		System.out.println(filePart.getSize());
		
		// 파일을 서버측에 업로드하는 처리 프로세스
		
		// 유효성 검사
		if(filePart == null || filePart.getSize() == 0) {
			response.setContentType("text/html");
			response.getWriter().println("첨부 파일을 추가해주세요");
			return;
		}
		
		// 사용자가 올린 파일원본 이름을 가져온다.
		
		 String originFileName = filePart.getSubmittedFileName();
		 System.out.println("originFileName : " + originFileName);
		
		// String originFileName = Paths.get(filePart.getSubmittedFileName()).toString();
		// System.out.println("originFileName : " + originFileName);
		
		
		// 1. 원본 파일명을 가져온다.
		// 2. 가능한 절대 중복되지 않을 이름을 만들어 준다.
		// UUID 를 통해서 고유한 파일명을 만들어 보자.
		// 3. 확장자를 분리해서 원본파일명 + _ + 고유한 UUID를 생성해서 새로운 파일명을 만들어 준다.
		String uniqueFileName = UUID.randomUUID().toString();
		
		// a.png, b.jpeg --> a_xhdf.png
		// 파일 확장자를 추출하여 고유한 파일명 뒤에 추가합니다.
		String extension = "";
		int i = originFileName.lastIndexOf(".");
		System.out.println("UNIQUE : " + uniqueFileName);
		System.out.println(". 인덱스 번호 : " + i);
		
		if(i > 0) {
			// . 포함한 확장자를 추출 합니다.
			extension = originFileName.substring(i);
			System.out.println("extension : " + extension);
		}
		
		uniqueFileName += extension;
		System.out.println(uniqueFileName);
		
		// 4. 어디에 저장할지 경로를 설정해야 한다.
		// C:\work_web\jsp_file_upload_ex1\src\main\webapp\images
		File uploadDirFile = new File("C:\\work_web\\jsp_file_upload_ex1\\src\\main\\webapp\\images");
		
		// 5. 해당 경로에 폴더가 존재하는지 확인 -> 없다면 폴더를 코드로 생성하기
		if(!uploadDirFile.exists()) {
			// 없으면 생성
			// mkdir <-- 폴더 생성 , mkdirs <-- 부모 폴더가 없으면 함께 생성해 준다.
			if(uploadDirFile.mkdirs()) {
				System.out.println("디렉토리가 생성 되었습니다. " + uploadDirFile);
			} else {
				throw new ServletException("디렉토리 생성에 실패했습니다.");
			}
		}
		
		// 파일 생성 ...
		// 앞에 적는게 경로,
		File fileToSave = new File(uploadDirFile, uniqueFileName);
		
		// 파일을 서버에 저장
		// filePart.write("경로");
		filePart.write(fileToSave.getAbsolutePath());
		
		// 응답 페이지 구성
		response.setContentType("text/html");
		response.getWriter().print("파일 업로드에 성공! ");
		response.getWriter().print("<br> ");
		response.getWriter().print("사용자가 올린 파일명 : " + originFileName);
		response.getWriter().print("<br> ");
		response.getWriter().print("서버에 저장된 파일명 : " +uniqueFileName);
		
		
		
	}

}

 

ImageListController.java

더보기

ImageListController.java

 

package com.tenco.controller;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

@WebServlet("/imageList")
public class ImageListController extends HttpServlet {
	private static final long serialVersionUID = 1L;
       
    public ImageListController() {
        super();
    }
    
    // 주소 설계
    // http://localhost:8080/imageList
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		// 이미지 파일이 저장된 경로를 가져 온다.
		String ulpoadDir = "C:\\work_web\\jsp_file_upload_ex1\\src\\main\\webapp\\images";
		File dir = new File(ulpoadDir);
		
		// 디렉토리 내 파일 리스트를 가져 오는 방법
		File[] files = dir.listFiles();
		List<String> fileNames = new ArrayList<String>();
		
		if(fileNames != null) {
			for(File f : files) {
				fileNames.add(f.getName());
			}
		}
		System.out.println(" file length : " + fileNames.size());
		System.out.println(" file names : " + fileNames.toString());
		
		request.setAttribute("fileNames", fileNames);
		request.getRequestDispatcher("/WEB-INF/list.jsp").forward(request, response);
		
		
	}
	
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
	}

}

list.jsp

더보기

list.jsp

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@page import="java.util.List"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<!--  주소 : http://localhost:8080/imageList -->
	<h1>이미지 리스트 출력하기 </h1>
	<ul>
	 <%
		List<String> fileNames = (List<String>)request.getAttribute("fileNames");
	 	if(fileNames != null && !fileNames.isEmpty() ){
	 		// 파일명들이 존재 한다면
	 		for(String fileName : fileNames) {
	 %>	
	 		<li><img src="images/<%=fileName%>" width="200px">
	 <%
	 	}
	 }	
	 %>
	</ul>
</body>
</html>