multiparty/form- data를 이용한 파일 업로드
- Gradle
plugins {
id 'java'
id 'org.springframework.boot' version '3.4.0'
id 'io.spring.dependency-management' version '1.1.6'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-mustache'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
developmentOnly 'org.springframework.boot:spring-boot-devtools'
runtimeOnly 'com.h2database:h2'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}
tasks.named('test') {
useJUnitPlatform()
}
- properties
server.servlet.encoding.charset=UTF-8
server.servlet.encoding.force=true
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:test
spring.datasource.username=sa
spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
- View - mustache
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Document</title>
</head>
<body>
<h1>사진 파일 전송</h1>
<hr>
<form action="/v1/upload" method="post" enctype="multipart/form-data">
<input type="text" name="username"><br>
<input type="file" name="img" accept="image/*"><br>
<input type="submit">
</form>
</body>
</html>
- WebConfig
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
WebMvcConfigurer.super.addResourceHandlers(registry);
// 1. 절대경로 file:///c:/upload/
// 2. 상대경로 file:./upload/
registry
.addResourceHandler("/upload/**") // html에서 경로를 적으면
.addResourceLocations("file:" + "./images/") // 웹서버의 /images/ 폴더 경로를 찾음
.setCachePeriod(60 * 60); // 초 단위 => 한시간
}
}
- Entity
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
@Getter
@Entity
@NoArgsConstructor
@Table(name = "upload_tb")
public class Upload {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String username;
private String profileUrl;
@Builder
public Upload(Integer id, String username, String profileUrl) {
this.id = id;
this.username = username;
this.profileUrl = profileUrl;
}
}
- DTO
import lombok.Data;
import org.springframework.web.multipart.MultipartFile;
public class UploadRequest {
@Data
public static class V1DTO{
private String username;
private MultipartFile img;
public Upload toEntity(String profile){
return Upload.builder().username(username).profileUrl(profile).build();
}
}
}
- Controller
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
@Controller
@RequiredArgsConstructor
public class UploadController {
private final UploadService uploadService;
// form 태그 이용
@GetMapping("/file1")
public String file1() {
return "file1";
}
// View에서 데이터를 v1DTO로 매핑해서 받음
@PostMapping("/v1/upload")
public String v1Upload(UploadRequest.V1DTO v1DTO) {
uploadService.v1사진저장(v1DTO);
return "index";
}
}
- Service
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
@RequiredArgsConstructor
@Service
public class UploadService {
private final UploadRepository uploadRepository;
@Transactional
public void v1사진저장(UploadRequest.V1DTO v1DTO){
// 1. DTO에 사진파일명을 롤링 시킨다.
// UUID를 사용하여 파일에 고유식별자를 붙인다.
String imgName = UUID.randomUUID()+"_"+v1DTO.getImg().getOriginalFilename();
String profileUrl = "images/"+imgName;
String dbUrl = "/upload/"+imgName;
// 2. DTO에 사진을 파일로 저장 (images 폴더)
try {
//images라는 폴더에 파일을 저장
Path path = Paths.get(profileUrl);
Files.write(path, v1DTO.getImg().getBytes());
// DB에 위치 + 고유식별자 + 파일이름을 저장
uploadRepository.save(v1DTO.toEntity(dbUrl));
} catch (IOException e) {
throw new RuntimeException(e.getMessage());
}
}
}
- Repository
import jakarta.persistence.EntityManager;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
@Repository
@RequiredArgsConstructor
public class UploadRepository {
private final EntityManager em;
public void save(Upload upload) {
em.persist(upload);
}
}
Share article