학습 목표
외부 라이브러리 없이 서블릿이 제공하는 자바 스펙으로 파일 업로드를 구현해 봅시다.
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>
'Java' 카테고리의 다른 글
| 2024.10.01 Stream API - 부록 3 (0) | 2024.10.01 |
|---|---|
| 2024.09.13 Java 유용한 클래스 - 3 람다식(Lambda expression) (1) | 2024.09.13 |
| 커스텀 태그(JSTL) 라이브러리 사용 jakarta.servlet.jsp.jstl 다운로드 (0) | 2024.07.12 |
| 커스텀 태그(JSTL) 라이브러리 사용 jakarta.servlet.jsp.jstl-api 다운로드 (0) | 2024.07.12 |
| 2024.07.12 JSP 프로그래밍 기본 커스텀 태그(JSTL) 라이브러리 사용, (EL 표현식) (5) | 2024.07.12 |