multipart/form-data 란?
multipart/form-data
는 폼 데이터가 여러 부분으로 나뉘어져 전송되는 인코딩 타입입니다. 주로 파일 업로드와 같이 큰 데이터를 포함한 폼을 서버로 전송할 때 사용됩니다. 이 방식은 각 부분이 경계(boundary)로 구분되며, 각각의 부분은 파일, 텍스트 필드 등 다양한 데이터를 포함할 수 있습니다.주요 특징
- 복수 부분:
- 폼 데이터는 여러 부분으로 나뉩니다. 각각의 부분은 텍스트 필드 값이나 파일을 포함할 수 있습니다.
- 각 부분은 경계(boundary) 문자열로 구분됩니다.
- 파일 업로드 지원:
- 파일과 같은 바이너리 데이터를 전송할 때 적합합니다.
- 텍스트 필드와 파일을 동일한 폼으로 함께 전송할 수 있습니다.
- 헤더 정보:
- 각 부분은 고유의 헤더를 가지며, 콘텐츠 타입, 이름 등의 정보가 포함됩니다.
- 예:
Content-Disposition: form-data; name="fieldname"; filename="filename.jpg"
예시
아래는
multipart/form-data
를 사용하여 텍스트 필드와 파일을 전송하는 예시입니다:<form action="/upload" method="post" enctype="multipart/form-data">
<input type="text" name="username"> <br>
<input type="file" name="file"> <br>
<input type="submit" value="Upload">
</form>
폼 태그
<form action="/v1/upload" method="post" enctype="multipart/form-data">
- action="/v1/upload": 폼이 제출될 때 데이터를 전송할 URL입니다. 여기서는 "/v1/upload"로 전송됩니다.
- method="post": 데이터를 전송할 HTTP 메서드를 지정합니다. POST 메서드는 데이터를 서버로 전송하는 데 사용됩니다.
- enctype="multipart/form-data": 파일과 같은 바이너리 데이터를 전송할 때 필요한 인코딩 타입입니다.
입력 필드
- 사용자 이름 입력:
- type="text": 텍스트 입력 필드를 생성합니다.
- name="username": 입력 필드의 이름을 지정합니다. 서버로 전송될 때 이 이름으로 값을 참조합니다.
`<input type="text" name="username"> <br>`
- 파일 업로드 입력:
- type="file": 파일 선택 입력 필드를 생성합니다.
- name="img": 입력 필드의 이름을 지정합니다. 사용자가 선택한 파일을 서버로 전송할 때 이 이름으로 파일을 참조합니다.
<input type="file" name="img"> <br>
- 제출 버튼:
- type="submit": 폼 제출 버튼을 생성합니다. 사용자가 이 버튼을 클릭하면 폼 데이터가 지정된
action
URL로 전송됩니다.
<input type="submit">
서버로 전송되는 데이터는 다음과 같이 구성됩니다:
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="username"
JohnDoe
------WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name="file"; filename="photo.jpg"
Content-Type: image/jpeg
(binary data)
------WebKitFormBoundary7MA4YWxkTrZu0gW--
여기서
boundary
는 각 부분을 구분하는 문자열입니다. 각 부분은 Content-Disposition
헤더로 이름과 파일 이름 등을 지정하며, 파일의 경우 Content-Type
헤더로 파일의 MIME 타입을 나타냅니다.데이터를 전달받고, DB에 저장하기
html파일 내용은 위의 form태그 예제와 동일합니다.
Controller :
@PostMapping("/v1/upload")
public String v1Upload(UploadRequest.V1DTO v1DTO) {
uploadService.v1사진저장(v1DTO);
return "index";
}
Service :
@Transactional
public void v1사진저장(UploadRequest.V1DTO v1DTO){
// static 메서드로 프로젝트에 이미지를 저장한 뒤 db에 저장할 dbUrl 문자열을 반환합니다.
String dbUrl = FileUtil.fileSave(v1DTO.getImg());
// db에 저장
uploadRepository.save(v1DTO.toEntity(dbUrl));
}
Service에서 사용한 fileSave 메서드 :
public class FileUtil {
public static String fileSave(MultipartFile file) {
// 1. DTO에 사진파일명을 롤링 시킨다.
String imgName = UUID.randomUUID()+"_"+file.getOriginalFilename();
String profileUrl = "images/"+imgName;
String dbUrl = "/upload/"+imgName;
// 2. DTO에 사진을 파일로 저장 (images 폴더)
try {
Path path = Paths.get(profileUrl);
Files.write(path, file.getBytes());
return dbUrl;
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
}
}
프로젝트 저장경로와 DB저장 경로
- 프로젝트에 저장하는 경로:
profileUrl
변수에 지정된"images/" + imgName
경로는 실제 파일이 저장되는 서버의 디렉토리를 나타냅니다.- 예:
images/unique-identifier_filename.jpg
- 이는 서버 내부의 디렉토리 경로로, 파일 시스템에서 직접 접근할 수 있는 경로입니다.
- 데이터베이스에 저장하는 경로:
dbUrl
변수에 지정된"/upload/" + imgName
경로는 웹에서 접근 가능한 URL을 나타냅니다.- 예:
/upload/unique-identifier_filename.jpg
- 이는 클라이언트가 파일에 접근할 수 있도록 서버에서 제공하는 URL입니다.
경로가 서로 다른 이유?
보안상의 이유로, 실제 파일이 저장된 경로와 클라이언트가 접근하는 경로를 분리하는 것이 일반적입니다. 이렇게 하면 다음과 같은 이점을 얻을 수 있습니다:
- 파일 보호: 서버의 내부 디렉토리 구조를 외부에 노출하지 않음으로써 파일 시스템을 보호할 수 있습니다.
- URL 관리: 웹 서버가 제공하는 URL을 통해 파일에 접근 권한을 제어할 수 있습니다. 예를 들어, 특정 사용자만 파일에 접근할 수 있도록 설정할 수 있습니다.
- 유연성: 파일 경로를 변경하더라도 클라이언트 측의 URL은 유지될 수 있습니다.
Repository :
public void save(Upload upload) {
em.persist(upload);
}
Share article