목차
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 타입은 바이너리 데이터를 나타내며, 서버가 이를 특별한 인코딩이나 디코딩 없이 원시 데이터로 취급하게 한다.
집중 !
HTTP 통신에서는 데이터가 기본적으로 바이트 단위로 전송됩니다. 컴퓨터에서 모든 데이터는 결국 이진수로 표현되며, HTTP 통신에서도 마찬가지로 1과 0으로 이루어진 바이트 단위의 데이터로 전송됩니다. 그런데 왜 통신에 있어 데이터가 문자 단위로 전송 된다고도 이야기를 할까?
처음 학습하는 학생들에게 쉽게 이해할 수 있도록 하기 위함 입니다. 예를 들어, HTML 문서나 JSON 같은 텍스트 형식의 데이터는 문자 단위로 이해할 수 있기 때문에, 이를 문자 단위로 전송한다고 설명하는 것이 쉽게 이해하는 데 도움이 주기 위함 입니다.
하지만 조금 더 자세히 말하자면, 이 문자들은 결국 특정 인코딩(예: UTF-8)을 통해 바이트로 변환되어 전송됩니다.
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 (부분 본문): 여기에는 실제 데이터가 들어간다. 예를 들어, 텍스트 필드의 내용이나 업로드된 파일의 내용이 포함된다.
시나리오 코드 1 (새 프로젝트 생성 - jsp_file_upload_ex)
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">
<!-- 파일을 전송하기 위한 설정 -->
<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>
package com.tenco.controller;
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;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.UUID;
/**
* 서블릿 스펙에서 파일 처리를 할려면
* 반드시 어노테이션 하나가 더 필요하다.
*/
@WebServlet("/upload")
@MultipartConfig // 반드시 선언
public class FileUploadController extends HttpServlet {
private static final long serialVersionUID = 1L;
public FileUploadController() {
super();
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// "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;
}
// 사용자가 올린 파일 원본 이름을 가져 온다.
// System.out.println("originFileName : " + originFileName);
String originFileName = filePart.getSubmittedFileName();
// 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);
System.out.println("fileToSave.getAbsolutePath() : " + fileToSave.getAbsolutePath());
// 파일을 서버에 저장
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);
}
}
이미지 뿌리기
package com.tenco.controller;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@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<>();
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 {
}
}
<%@ 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>
<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 > JSP' 카테고리의 다른 글
JSP 목차 (0) | 2024.07.15 |
---|---|
JSTL 을 활용한 게시판 기능 만들기 (0) | 2024.07.15 |
커스텀 태그(JSTL) 라이브러리 사용, (EL 표현식) (1) | 2024.07.12 |
JSP와 MVC 패턴 Todo 프로젝트 (0) | 2024.07.09 |
JSP(교재) 웹 프로그래밍 기초 (0) | 2024.07.09 |