multipart/form-data 란?
HTTP 프로토콜에서 파일 업로드나 여러 종류의 데이터를 한 번의 요청으로 전송하기 위해 사용되는 타입이다.
데이터를 여러 부분(Part)으로 나누어 전송하며, 각 부분은 고유한 헤더와 본문을 가진다.
예제
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="text" name="username" placeholder="Enter your name">
<input type="file" name="profile_picture">
<button type="submit">Submit</button>
</form>
input 태그의 name 속성과 데이터들이 ----- 로 구분되어 보여진다.
마지막 구분선에서는 boundary 값 뒤에 --를 붙여 데이터의 끝을 표시해준다.
--------------------------boundary
Content-Disposition: form-data; name="username"
JohnDoe
--------------------------boundary
Content-Disposition: form-data; name="profile_picture"; filename="avatar.png"
Content-Type: image/png
(binary content of the file)
--------------------------boundary--
Boundary
- 각 파트를 구분하는 고유 문자열
- 요청 헤더의 Content-Type에 포함되며, 요청 바디에서도 각 파트를 구분하는 데 사용된다.
Content-Disposition
- 각 파트의 데이터가 무엇을 의미하는지 정의한다.
- 필드 이름(name)과 파일 이름(filename)을 포함할 수 있다.
- name : HTML form의 input 태그 이름
- filename : 업로드된 파일 이름
Content-Type
- 각 파트의 데이터 형식을 지정한다.
- text/plain, image/jpeg, application/json
- 텍스트 필드(input type=text)에는 생략될 수 있다.
form에서 input 태그로 파일 보내기
<input> 태그에 타입을 file로 넣으면 파일 선택이 표시되고, 파일을 선택하면 파일이 보여진다.
뒤에 multiple이 붙으면 한 번에 여러 파일을 업로드할 수 있도록 지원한다.
<form action="/product/register" method="post" enctype="multipart/form-data">
<input type="text" name="username" placeholder="Enter your name">
<input type="file" name="image" multiple>
<button type="submit">Submit</button>
</form>
서버에서 HTTP 요청 처리
Part 인터페이스는 multipart/form-data POST 요청으로 받은 form 아이템이나 하나의 Part를 나타낸다.
하나의 파일을 받을 때는 req.getPart(name 속성), 여러 개를 받을 때는 getParts() 를 사용한다.
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String action = req.getPathInfo();
if ("/register".equals(action)) {
// 파일 하나
Part part = req.getPart("image");
// 파일 여러 개
List partList = (List) req.getParts();
Product product = new Product(
req.getParameter("name"),
Integer.parseInt(req.getParameter("original_price")),
Integer.parseInt(req.getParameter("price")),
req.getParameter("description"),
Integer.parseInt(req.getParameter("quantity"))
);
productService.register(partList, product);
resp.sendRedirect("/product/list");
}
}
여러 개의 파일을 처리할 때는 List로 매개변수를 받은 다음, 반복문을 돌면서 Part 에 getName() 메소드를 실행해준다.
getName() 메소드는 name 속성을 반환해준다.
ProductService.java
public void register(List partList, Product product) {
int productIdx = productDao.save(product);
for(Object part : partList) {
if(((Part) part).getName().equals("image")) {
String savedPath = UploadUtil.upload((Part) part);
productDao.saveImage(savedPath, productIdx);
}
}
}
파라미터로 받은 파일을 폴더에 Upload
static 변수로 파일을 업로드할 폴더 경로를 저장해준다.
part.write 메소드의 매개변수로 저장해준 폴더 경로 + 파일명 값을 보내준다.
매개변수로 받은 경로에 파일을 업로드해준다.
public class UploadUtil {
private final static String uploadPath = "C:/class/projects/java/shop/web/static/upload/";
public static String upload(Part part) {
try{
part.write(uploadPath + part.getSubmittedFileName());
}catch (IOException e){
throw new RuntimeException(e);
}
return "/static/upload/" + part.getSubmittedFileName();
}
}
리턴된 파일 경로를 savedPath 변수에 저장해주고, productDto의 saveImage 메소드 매개변수로 보내준다.
String savedPath = UploadUtil.upload((Part) part);
productDao.saveImage(savedPath, productIdx);
DB에 파일 데이터 저장
매개변수로 받은 파일 경로를 PreparedStatement를 사용해서 sql문에 넣어준다.
ProductDao.java
public void saveImage(String savedPath, int productIdx) {
String sql = "INSERT INTO product_image (image_url, product_idx) VALUES (?, ?)";
try {
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setString(1,savedPath);
pstmt.setInt(2, productIdx);
pstmt.executeUpdate();
} catch (SQLException e) {
throw new RuntimeException(e);
}
}
'BE > Java' 카테고리의 다른 글
[Java] 카카오 로그인 API 연동 (0) | 2025.01.10 |
---|---|
[Java] DTO 설계 시 고려사항 (0) | 2025.01.10 |
[Java] SQL Injection, PreparedStatement (2) | 2025.01.09 |
[Java] Servlet 프로젝트 구조 / 로그인, 회원가입 기능 구현 (0) | 2025.01.08 |
[Java] IntelliJ + MariaDB 연동, SQL 실행 (0) | 2025.01.08 |