BE/Java

[Java] multipart/form-data란? / form 태그로 서버에 파일 전달하기

셰욘 2025. 1. 9. 19:29
728x90

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>

 

input type="file"

 

파일 선택 시 파일 경로가 넣어짐

 

 

 

 


서버에서 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);
    }
}

 

728x90