클라이언트 - 서버
간 요청과 응답을 통하여 정보를 주고받을 때 사용하는 통신 규약이다.
어디로
, 어떻게
보낼지를 정의하는 태그이다.폼 데이터
를 서버로 보낼 때 인코딩 되는 방식
을 결정한다.application/x-www-form-urlencoded
(디폴트)enctype
속성이 입력되지 않았거나 application/x-www-form-urlencoded
로 입력하면 Content-Type: application/x-www-form-urlencoded
이 HTTP Header에 추가된다.&
로 나눠서 나열해서 보낸다name=dooboo&age=5
→ name:dooboo, age:5
multipart/form-data
Content-TypeL multipart.form data; boundary= ---...
이 HTTP Header에 추가된다.POST / HTTP/1.1
...
Content-type: multipart/form-data; boundary=---------------------------asd123456
...
--------------------------asd123456
content-disposition : form-data; name="file"; filename="테스트.png"
content-type : image/png
...
9��7�xCL�vp�c��38�c8Z��K��id))WR%���N...(바이너리 데이터)
--------------------------asd123456
...
--------------------------asd123456
...
--------------------------asd123456--
--- ...
부분은 Part
들을 나누기 위한 구분자이다.--
를 추가적으로 붙인다.multipart
로 전송된 데이터를 반환하는 메소드multipart
로 넘어온 폼 데이터의 각각이 Part
이다.part.getHeaderNames()
part.getHeader(String headerName)
headerName
이름을 가진 헤더 정보를 가져온다.part.getInpuStream();
part.getSubmittedFileName()
part.write(String path)
@Slf4j
@Controller
public class SaveTestController {
@PostMapping("/save")
public String save(HttpServletRequest request) throws ServletException, IOException {
log.info("request={}", request);
String fileDir = "파일저장할 경로";
String itemName = request.getParameter("itemName");
log.info("itemName={}", itemName);
Collection<Part> parts = request.getParts();
log.info("parts={}", parts);
for (Part part : parts) {
Collection<String> headerNames = part.getHeaderNames();
// 파일 명
log.info("submittedFileName={}", part.getSubmittedFileName());
// 크기 (용량)
log.info("size={}", part.getSize());
//테이터 읽기
String body = StreamUtils.copyToString(part.getInputStream(), StandardCharsets.UTF_8);
log.info("body={}", body);
//파일에 저장하기
if (StringUtils.hasText(part.getSubmittedFileName())) {
String fullPath = fileDir + part.getSubmittedFileName();
log.info("파일 저장 fullPath={}", fullPath);
part.write(fullPath);
}
}
return "...";
}
}
multipart/data-form
으로 넘어온 파일
을 사용하기 위한 인터페이스multipartFile.getOriginalFilename()
multipartFile.transferTo(File file)
File file
에 파일을 작성한다.@RequestParam
, @ModelAttribute
를 통하여 해당 파일 파라미터를 변수나 필드에 바인딩해서 사용할 수 있다.<input type="file" name="file">
@PostMapping("/fileUpload")
public String fileUpload(@RequestParam List<MultipartFile> file) throws IOException {
String fileDir = "파일 저장 경로";
if (!file.isEmpty()) {
// 이 위치에 파일 저장
String fullPath = fileDir + file.getOriginalFilename();
file.transferTo(new File(fullPath));
// 파일 저장 후 String fullPath와 파일 정보를 레파지토리의 save하는 과정이 필요함
// 예를 들면, HosImg엔티티를 생성하고 영속성 컨텍스트에 저장하는 과정...
}
return "...";
}
<form th:action method="post" th:object="${hospitalSaveForm}" enctype="multipart/form-data">
<div>
<label for="hosName">병원명</label>
<input type="text" th:field="*{hosName}" placeholder="입력하세요">
</div>
<!-- 다른 파라미터 받는 인풋 태그-->
<div>
<label for="hosImgs">사진 등록</label>
<input type="file" th:field="*{hosImgs}" multiple>
<!-- 이미지 목록을 나타낼 태그 -->
<div id="hosImgsList"></div>
</div>
<div>
<button type="submit"> 등록</button>
</div>
</form>
@Data
public class HospitalSaveForm {
@NotBlank
private String hosName;
// ...
private List<MultipartFile> hosImgs;
}
@PostMapping(value = "/add")
public String add (HospitalSaveForm hospitalSaveForm) {
//예외처리 생략
/*병원 저장*/
Long hospitalId = hospitalService.registerHos(hospitalSaveForm);
return "redirect:/hospital/detail/" + hospitalId;
}
병원
과 병원 이미지
를 영속성 컨텍스트에 저장하는 과정@Transactional
public Long registerHos(HospitalSaveForm hospitalSaveForm) {
// 병원 엔티티 생성
Hospital hospital = Hospital.createHospital(...);
// 병원을 저장 (병원 영속성 시작)
hospitalRepository.save(hospital);
// 병원 이미지 저장
for (MultipartFile file : files) {
if(!file.isEmpty()) {
HosImg hosImg = HosImg.createHosImg(hospital, file);
hosImgRepository.save(hosImg);
}
}
return hospital.getHosId();
}
@Slf4j
@Entity
@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class HosImg extends TimeStamped {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long himId;
@ManyToOne(fetch = LAZY)
@JoinColumn(name = "hosId") //hosImg 테이블의 컬럼명
private Hospital hospital; //hospital PK
@Column(length = 1000)
private String himPath;
private boolean himMain;
@Column(length = 1000)
private String himOrigin;
/*== 생성 메소드 ==*/
public static HosImg createHosImg(Hospital hospital, MultipartFile file){
String path = file.getOriginalFilename();
String savedPath = "파일 저장 경로";
File existChk = new File(savedPath);
if(!existChk.exists()) existChk.mkdirs(); //해당 경로가 없다면 디렉토리 만들기
String savedFile = UUID.randomUUID().toString().replaceAll("-", "") // 서버에 저장할 파일 명으로 쓸 UUID (중복방지)
+ path.substring(path.lastIndexOf(".")); // 확장자명
try {
file.transferTo(new File(savedPath, savedFile)); // 서버에 파일로서 저장
} catch (IOException e) {
e.printStackTrace();
}
return HosImg.builder()
.himPath("/images/upload/" + savedFile)
.hospital(hospital)
.himOrigin(path)
.build();
}
}