Category: Spring

0

Spring Boot - Resource 추상화

목차 Spring Boot - StreamingResponseBody Spring Boot - ResourceRegion Spring Boot - 파일 다운로드 서비스 구현하기 Spring Boot - 파일 업로드 서비스 구현하기 Spring Boot - Resource 추상화 Spring Boot - MultipartFile 에서 발생하는 예외 처리 Spring Boot - MultipartFile 를 이용한 파일 업로드 Spring Boot - Part 객체를 이용한 파일 업로드 Spring boot Resource 추상화https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/resources.html Resource 는 java.net.URL 을 추상화 해 스프링 프레임워크에서 Low-Level에 있는 자원에 쉽게 접근할 수 있도록 지원한다 ClassPath를 기준으로 리소스를 가져오는 기능의 부재 ServletContext를 기준으로 상대 경로를 읽어오는 기능의 부재 Resource.java public interface Resource extends InputStreamSource { boolean exists(); URL getURL() throws IOException; URI getURI() throws IOException; File getFile() throws IOException; long contentLength() throws IOException; long lastModified() throws IOException; Resource createRelative(String relativePath) throws IOException; @Nullable String getFilename(); String getDescription(); default boolean isReadable() { return exists(); } default boolean isOpen() { return false; } default boolean isFile() { return false; } default ReadableByteChannel readableChannel() throws IOException { return Channels.newChannel(getInputStream()); }} Resource 구현체

0

Spring Boot - 파일 다운로드 서비스 구현하기

목차 Spring Boot - StreamingResponseBody Spring Boot - ResourceRegion Spring Boot - 파일 다운로드 서비스 구현하기 Spring Boot - 파일 업로드 서비스 구현하기 Spring Boot - Resource 추상화 Spring Boot - MultipartFile 에서 발생하는 예외 처리 Spring Boot - MultipartFile 를 이용한 파일 업로드 Spring Boot - Part 객체를 이용한 파일 업로드 파일 다운로드를 위한 HTTP 해더서버에 저장된 파일을 다운 받기 위해서는 Response 해더에 Content-Disposition 정보가 필요합니다. Content-Disposition 는 HTTP 프로토콜에서 사용되는 헤더로, 서버가 클라이언트에게 제공하는 콘텐츠가 브라우저에 표시될지 또는 다운로드할지, 그리고 다운로드될 경우 파일 이름은 무엇인지 등을 지정합니다. Content-Disposition 는 inline, attachment 두 가지 종류가 있습니다. Content-Disposition 값이 inline 면 콘텐츠가 브라우저에서 직접 표시됩니다. attachment 면 콘텐츠가 다운로드를 위한 파일로 제공됩니다. filename 속성을 이용하면 다운로드하기 위한 파일이름을 지정할 수 있습니다. // Content-Disposition 값이 inline 일 경우 브라우저에 직접 표시됩니다.Content-Disposition: inline// attachment 일 경우 리소스를 파일로 다운로드할 수 있습니다. filename 속성을 통해 파일 이름을 지정할 수 있습니다.Content-Disposition: attachment; filename="example.txt" 리소스 다운로드 서비스 구현리소스를 반환하는 Response 객체에 Content-Disposition 해더를 추가해줍니다.

0

Spring Cloud - 33. Order Kafka Producer

목차 Spring Cloud - 33. Order Kafka Producer Spring Cloud - 32. kafka-mariadb 연결 Spring Cloud - 31. Kafka Topic 적용 Order Kafka Producer@RestController@RequestMapping("/order-service/")@RequiredArgsConstructorpublic class OrderController { private final OrderService orderService; private final KafkaProducer kafkaProducer; private final OrderProducer orderProducer; @PostMapping(value = "/{userId}/orders") public ResponseEntity<ResponseOrder> createOrder(@PathVariable("userId") String userId, @RequestBody RequestOrder orderDetails){ ModelMapper modelMapper = new ModelMapper(); modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT); OrderDto orderDto = modelMapper.map(orderDetails, OrderDto.class); orderDto.setUserId(userId); orderDto.setOrderId(UUID.randomUUID().toString()); orderDto.setTotalPrice(orderDetails.getQty() * orderDetails.getUnitPrice()); ResponseOrder returnValue = modelMapper.map(orderDto, ResponseOrder.class); kafkaProducer.send("example-order-topic", orderDto); orderProducer.send("orders", orderDto); return ResponseEntity.status(HttpStatus.CREATED).body(returnValue); } @GetMapping(value = "/{userId}/orders") public ResponseEntity<List<ResponseOrder>> getOrder(@PathVariable("userId") String userId){ Iterable<OrderEntity> orderList = orderService.getOrdersByUserId(userId); List<ResponseOrder> result = new ArrayList<>(); orderList.forEach(v -> { result.add(new ModelMapper().map(v, ResponseOrder.class)); }); return ResponseEntity.status(HttpStatus.OK).body(result); }} @Service@Slf4j@RequiredArgsConstructorpublic class OrderProducer { private final KafkaTemplate<String, String> kafkaTemplate; public OrderDto send(String topic, OrderDto orderDto){ ObjectMapper mapper = new ObjectMapper(); String jsonInString = ""; try{ jsonInString = mapper.writeValueAsString(orderDto); }catch (JsonProcessingException ex){ ex.printStackTrace(); } kafkaTemplate.send(topic, jsonInString); log.info("Kafka Producer sent data from the Order microservice: " + orderDto); return orderDto; }} @Data@Builderpublic class Scheme { private String type; private List<Field> fields; private boolean optional; private String name;} @Data@AllArgsConstructorpublic class Field { private String type; private boolean optional; private String field;} @Data@Builderpublic class Payload { private String order_id; private String user_id; private String product_id; private int qty; private int unit_price; private int total_price;} @Data@AllArgsConstructorpublic class KafkaOrderDto implements Serializable { private Scheme scheme; private Payload payload;}

0

Spring Cloud - 32. kafka-mariadb 연결

목차 Spring Cloud - 33. Order Kafka Producer Spring Cloud - 32. kafka-mariadb 연결 Spring Cloud - 31. Kafka Topic 적용 Config// https://mvnrepository.com/artifact/org.mariadb.jdbc/mariadb-java-clientimplementation 'org.mariadb.jdbc:mariadb-java-client' create table users( id int auto_increment primary key, user_id varchar(20), pwd varchar(20), name varchar(20), created_at datetime default NOW()); Kafka Connect 설치curl -O http://packages.confluent.io/archive/5.5/confluent-community-5.5.2-2.12.tar.gz curl -O http://packages.confluent.io/archive/6.1/confluent-community-6.1.0.tar.gz tar xvf confluent-community-6.1.0.tar.gz

0

Spring Cloud - 31. Kafka Topic 적용

목차 Spring Cloud - 33. Order Kafka Producer Spring Cloud - 32. kafka-mariadb 연결 Spring Cloud - 31. Kafka Topic 적용 Catalog Service// https://mvnrepository.com/artifact/org.springframework.kafka/spring-kafkaimplementation 'org.springframework.kafka:spring-kafka' @EnableKafka@Configurationpublic class KafkaConsumerConfig { @Bean public ConsumerFactory<String, String> consumerFactory(){ Map<String, Object> properties = new HashMap<>(); properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "127.0.0.1:9092"); properties.put(ConsumerConfig.GROUP_ID_CONFIG, "consumerGroupId"); properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class); return new DefaultKafkaConsumerFactory<>(properties); } @Bean public ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory(){ ConcurrentKafkaListenerContainerFactory<String, String> kafkaListenerContainerFactory = new ConcurrentKafkaListenerContainerFactory<>(); kafkaListenerContainerFactory = new ConcurrentKafkaListenerContainerFactory<>(); kafkaListenerContainerFactory.setConsumerFactory(consumerFactory()); return kafkaListenerContainerFactory; }} Order Service@Configurationpublic class KafkaProducerConfig { @Bean public ProducerFactory<String, String> producerFactory(){ Map<String, Object> configProps = new HashMap<>(); configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "127.0.0.1:9092"); configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class); configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class); return new DefaultKafkaProducerFactory<>(configProps); } @Bean public KafkaTemplate<String, String> kafkaTemplate(){ return new KafkaTemplate<>(producerFactory()); }} @Service@Slf4j@RequiredArgsConstructorpublic class KafkaProducer { private final KafkaTemplate<String, String> kafkaTemplate; public OrderDto send(String kafkaTopic, OrderDto orderDto){ ObjectMapper mapper = new ObjectMapper(); String jsonInString = ""; try{ jsonInString = mapper.writeValueAsString(orderDto); }catch (JsonProcessingException e){ e.printStackTrace(); } kafkaTemplate.send(kafkaTopic, jsonInString); log.info("Kafka Producer send data from the Order microservice: " + orderDto); return orderDto; }}

0

Spring Cloud - 30. Micro Service간 통신 ErrorDecoder 구현

목차 Spring Cloud - 30. Micro Service간 통신 ErrorDecoder 구현 Spring Cloud - 29. Micro Service간 통신 Feign client 예외 처리 Spring Cloud - 28. Micro Service간 통신 Feign client Spring Cloud - 27. Micro Service간 통신 ErrorDecoder 구현public class FeignErrorDecoder implements ErrorDecoder { @Override public Exception decode(String methodKey, Response response) { switch (response.status()) { case 400: break; case 404: if (methodKey.contains("getOrders")) { return new ResponseStatusException(HttpStatus.valueOf(response.status()) , "User's orders is empty."); } break; default: return new Exception(response.reason()); } return null; }} @SpringBootApplication@EnableDiscoveryClient@EnableFeignClients@Slf4jpublic class UserServiceApplication { public static void main(String[] args){ SpringApplication.run(UserServiceApplication.class, args); } @Bean public BCryptPasswordEncoder passwordEncoder() throws UnknownHostException { return new BCryptPasswordEncoder(); } @Bean @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); } @Bean public Logger.Level feignLoggerLevel(){ return Logger.Level.FULL; } @Bean public FeignErrorDecoder getFeignErrorDecoder(){ return new FeignErrorDecoder(); }} token: expiration_time: 84600000 secret: user_token_native_application_changedgateway: ip: 192.168.123.106order_service: url: http://ORDER-SERVICE/order-service/%s/orders exception: orders_is_empty: User's orders is empty. @Component@RequiredArgsConstructorpublic class FeignErrorDecoder implements ErrorDecoder { private final Environment env; @Override public Exception decode(String methodKey, Response response) { switch (response.status()) { case 400: break; case 404: if (methodKey.contains("getOrders")) { return new ResponseStatusException(HttpStatus.valueOf(response.status()) , env.getProperty("order_service.exception.orders_is_empty")); } break; default: return new Exception(response.reason()); } return null; }}

0

Spring Boot - 파일 업로드 서비스 구현하기

목차 Spring Boot - StreamingResponseBody Spring Boot - ResourceRegion Spring Boot - 파일 다운로드 서비스 구현하기 Spring Boot - 파일 업로드 서비스 구현하기 Spring Boot - Resource 추상화 Spring Boot - MultipartFile 에서 발생하는 예외 처리 Spring Boot - MultipartFile 를 이용한 파일 업로드 Spring Boot - Part 객체를 이용한 파일 업로드 DTO 생성하기@Datapublic class Item { private Long id; private String itemName; private UploadFile attachFile; // 여러개의 이미지 파일을 입력하기 위한 List private List<UploadFile> imageFiles;} 파일을 업로드 할때 중복된 이름을 가진 파일이 있을 경우 해당 파일로 업데이트 되는 문제가 발생합니다. 이를 해결하기 위해 파일을 저장할때는 다름 이름으로 저장하고 원래 파일이름을 별도로 저장합니다. 저장된 파일이름과 매핑되는 원래 파일 이름을 가져올 수 있도록 UploadFile 객체를 생성합니다. @Datapublic class UploadFile { private String uploadFileName; private String storeFileName; public UploadFile(String uploadFileName, String storeFileName) { this.uploadFileName = uploadFileName; this.storeFileName = storeFileName; }} 파일 저장 - 서비스 구현파일에서 확장자 추출하기

0

Spring Boot - MultipartFile 를 이용한 파일 업로드

목차 Spring Boot - StreamingResponseBody Spring Boot - ResourceRegion Spring Boot - 파일 다운로드 서비스 구현하기 Spring Boot - 파일 업로드 서비스 구현하기 Spring Boot - Resource 추상화 Spring Boot - MultipartFile 에서 발생하는 예외 처리 Spring Boot - MultipartFile 를 이용한 파일 업로드 Spring Boot - Part 객체를 이용한 파일 업로드 MultipartFile 인터페이스 multipart/form-data 요청을 처리하기 위해 만들어진 객체 Spring 에서는 멀티파트 요청을 처리 하기 위해 MultipartFile 객체를 제공합니다. public interface MultipartFile extends InputStreamSource { // 전달 받은 Parameter Key값을 가져온다. String getName(); // 파일 명을 가져온다. @Nullable String getOriginalFilename(); // 파일의 ContentType을 가져온다. // pdf 파일의 경우 application/pdf, mp4 파일의 경우 video/mp4, png 파일의 경우 image/png @Nullable String getContentType(); // 전달 받은 Parameter Value값이 비어 있는지 확인한다. boolean isEmpty(); // 전달 받은 파일의 크기를 가져온다. // 파일은 byte 단위로 표시된다. long getSize(); byte[] getBytes() throws IOException; // 전달 받은 파일을 읽기 위한 InputStream을 가져온다. @Override InputStream getInputStream() throws IOException; // MultipartFile 객체를 이용해 Resource(MultipartFileResource) 객체를 반환 받는다. default Resource getResource() { return new MultipartFileResource(this); } // MultipartFile 내 데이터를 전달받은 File 객체에 저장한다. // 한번만 호출이 가능한 메소드다. void transferTo(File dest) throws IOException, IllegalStateException; // MultipartFile 내 데이터를 전달받은 Path 객체에 저장한다. default void transferTo(Path dest) throws IOException, IllegalStateException { FileCopyUtils.copy(getInputStream(), Files.newOutputStream(dest)); }} 파일 업로드 구현파일 업로드를 위한 Controller 구현 multipart/form-data 요청은 Form 형식으로 요청이 오기 때문에 RequestBody 어노테이션이 아니라 RequestParam 어노테이션을 이용해 데이터를 받는다.

0

Spring Cloud - 29. Micro Service간 통신 Feign client 예외 처리

목차 Spring Cloud - 30. Micro Service간 통신 ErrorDecoder 구현 Spring Cloud - 29. Micro Service간 통신 Feign client 예외 처리 Spring Cloud - 28. Micro Service간 통신 Feign client Spring Cloud - 27. Micro Service간 통신 User serviceserver: port: 0spring: application: name: user-service h2: console: enabled: true settings: web-allow-others: true datasource: username: sa password: driver-class-name: org.h2.Driver url: jdbc:h2:mem:testdb rabbitmq: host: 127.0.0.1 port: 5672 username: guest password: guesteureka: instance: instanceId: ${spring.application.name}:${spring.application.instance_id:${random.value}} client: register-with-eureka: true fetch-registry: true service-url: defaultZone: http://localhost:8761/eurekagreeting: message: Welcome to the Simple E-commerce.management: endpoints: web: exposure: include: refresh, health, beans, busrefreshlogging: level: com.example.userservice.client: DEBUG#token:# expiration_time: 84600000# secret: user_token @SpringBootApplication@EnableDiscoveryClient@EnableFeignClients@Slf4jpublic class UserServiceApplication { public static void main(String[] args){ SpringApplication.run(UserServiceApplication.class, args); } @Bean public BCryptPasswordEncoder passwordEncoder() throws UnknownHostException { return new BCryptPasswordEncoder(); } @Bean @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); } @Bean public Logger.Level feignLoggerLevel(){ return Logger.Level.FULL; }} @Overridepublic UserDto getUserByUserId(String userId) { UserEntity userEntity = userRepository.findByUserId(userId); if (userEntity == null) throw new UsernameNotFoundException("User not found"); UserDto userDto = new ModelMapper().map(userEntity, UserDto.class); List<ResponseOrder> orderList = null; try { orderList = orderServiceClient.getOrders(userId); } catch (FeignException ex) { log.error(ex.getMessage()); } userDto.setOrders(orderList); return userDto;}

0

Spring Cloud - 28. Micro Service간 통신 Feign client

목차 Spring Cloud - 30. Micro Service간 통신 ErrorDecoder 구현 Spring Cloud - 29. Micro Service간 통신 Feign client 예외 처리 Spring Cloud - 28. Micro Service간 통신 Feign client Spring Cloud - 27. Micro Service간 통신 User service// https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-openfeignimplementation 'org.springframework.cloud:spring-cloud-starter-openfeign' @SpringBootApplication@EnableDiscoveryClient@EnableFeignClients@Slf4jpublic class UserServiceApplication { public static void main(String[] args){ SpringApplication.run(UserServiceApplication.class, args); } @Bean public BCryptPasswordEncoder passwordEncoder() throws UnknownHostException { return new BCryptPasswordEncoder(); } @Bean @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); }} FeignClient의 name속성에 들어가는 값은 eureka 서버에 등록 돼 있는 Application 이름이다. @FeignClient(name = "order-service")public interface OrderServiceClient { @GetMapping("/order-service/{userId}/orders") List<ResponseOrder> getOrders(@PathVariable String userId);} @Overridepublic UserDto getUserByUserId(String userId) { UserEntity userEntity = userRepository.findByUserId(userId); if (userEntity == null) throw new UsernameNotFoundException("User not found"); UserDto userDto = new ModelMapper().map(userEntity, UserDto.class); List<ResponseOrder> orderList = orderServiceClient.getOrders(userId); userDto.setOrders(orderList); return userDto;}

0

Spring Cloud - 27. Micro Service간 통신

목차 Spring Cloud - 30. Micro Service간 통신 ErrorDecoder 구현 Spring Cloud - 29. Micro Service간 통신 Feign client 예외 처리 Spring Cloud - 28. Micro Service간 통신 Feign client Spring Cloud - 27. Micro Service간 통신 User Service@SpringBootApplication@EnableDiscoveryClient@Slf4jpublic class UserServiceApplication { public static void main(String[] args){ SpringApplication.run(UserServiceApplication.class, args); } @Bean public BCryptPasswordEncoder passwordEncoder() throws UnknownHostException { return new BCryptPasswordEncoder(); } @Bean public RestTemplate getRestTemplate(){ return new RestTemplate(); }} @Overridepublic UserDto getUserByUserId(String userId) { UserEntity userEntity = userRepository.findByUserId(userId); if (userEntity == null) throw new UsernameNotFoundException("User not found"); UserDto userDto = new ModelMapper().map(userEntity, UserDto.class); // List<ResponseOrder> orders = new ArrayList<>(); // userDto.setOrders(orders); String orderUrl = String.format(env.getProperty("order_service.url"), userId); ResponseEntity<List<ResponseOrder>> orderListResponse = restTemplate.exchange(orderUrl , HttpMethod.GET , null , new ParameterizedTypeReference<List<ResponseOrder>>() {}); List<ResponseOrder> orderList = orderListResponse.getBody(); userDto.setOrders(orderList); return userDto;} application.ymltoken: expiration_time: 84600000 secret: user_token_native_application_changedgateway: ip: 192.168.123.106order_service: url: http://127.0.0.1:8080/order-service/%s/orders token: expiration_time: 84600000 secret: user_token_native_application_changedgateway: ip: 192.168.123.106order_service: url: http://ORDER-SERVICE/order-service/%s/orders @SpringBootApplication@EnableDiscoveryClient@Slf4jpublic class UserServiceApplication { public static void main(String[] args){ SpringApplication.run(UserServiceApplication.class, args); } @Bean public BCryptPasswordEncoder passwordEncoder() throws UnknownHostException { return new BCryptPasswordEncoder(); } @Bean @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); }}

0

Spring Cloud - 26. Spring Cloud Bus

Config Server// https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-actuatorimplementation 'org.springframework.boot:spring-boot-starter-actuator' // https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-bus-amqpimplementation 'org.springframework.cloud:spring-cloud-starter-bus-amqp' Users Microservice// https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-bus-amqpimplementation 'org.springframework.cloud:spring-cloud-starter-bus-amqp' Gateway Service// https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-bus-amqpimplementation 'org.springframework.cloud:spring-cloud-starter-bus-amqp' Config Serverserver: port: 8888spring: application: name: config-service profiles: active: native cloud: config: server: native: search-locations: file://${user.home}/dev/study/Spring-Cloud/gateway/git-local-repository rabbitmq: host: 127.0.0.1 port: 5672 username: guest password: guestmanagement: endpoints: web: exposure: include: health, busrefresh#spring:# application:# name: config-service# cloud:# config:# server:# git:# uri: https://github.com/ckck803/spring-cloud-config## uri: file:///Users/dongwoo-yang/dev/study/Spring-Cloud/gateway/git-local-repository User Service

0

Spring Cloud - 25. Spring Cloud Config Native

목차 Spring Cloud - 25. Spring Cloud Config Native Spring Cloud - 24. Client Service 에서 Config Server 연동하기 Spring Cloud - 23. Spring Cloud Config Server 만들기 Spring Cloud - 22. Spring Cloud Config 연동 Actuator 2 Spring Cloud - 21. Spring Cloud Config 연동 Actuactor server: port: 8888spring: application: name: config-service profiles: active: native cloud: config: server: native: search-locations: file://${user.home}/dev/study/Spring-Cloud/gateway/git-local-repository#spring:# application:# name: config-service# cloud:# config:# server:# git:# uri: https://github.com/ckck803/spring-cloud-config## uri: file:///Users/dongwoo-yang/dev/study/Spring-Cloud/gateway/git-local-repository

0

Spring Cloud - 24. Client Service 에서 Config Server 연동하기

목차 Spring Cloud - 25. Spring Cloud Config Native Spring Cloud - 24. Client Service 에서 Config Server 연동하기 Spring Cloud - 23. Spring Cloud Config Server 만들기 Spring Cloud - 22. Spring Cloud Config 연동 Actuator 2 Spring Cloud - 21. Spring Cloud Config 연동 Actuactor Client Service 에서 Config Server 로부터 Config 파일 읽어오기// 외부 Config를 가져오기 위한 Dependency 설정implementation 'org.springframework.cloud:spring-cloud-config-server'implementation 'org.springframework.cloud:spring-cloud-starter-bootstrap' spring: cloud: config: uri: http://127.0.0.1:8888 name: ecommerce profiles: active: dev spring: cloud: config: uri: http://127.0.0.1:8888 name: ecommerce profiles: active: prod

0

Spring Boot - 파일 업로드 2

목차 Spring Boot - StreamingResponseBody Spring Boot - ResourceRegion Spring Boot - 파일 다운로드 서비스 구현하기 Spring Boot - 파일 업로드 서비스 구현하기 Spring Boot - Resource 추상화 Spring Boot - MultipartFile 에서 발생하는 예외 처리 Spring Boot - MultipartFile 를 이용한 파일 업로드 Spring Boot - Part 객체를 이용한 파일 업로드 Spring boot 파일 업로드 2 getHeaderNames() part Type으로 전달된 데이터의 모든 해더 이름을 가져온다. getHeader(headerName) getHeader메소드에 헤더 이름을 넘겨주면 해더 이름이 갖는 요청의 모든 해더 정보를 가져온다. getSubmittedFileName() 파일 명을 가져오기 위한 메소드 getInputStream() Part에 저장된 데이터를 가져오기 위한 메소드 write(filepath) write 메소드에 파일이 저장될 File Path를 입력하면 해당 경로에 파일이 저장하는 메소드 Collection<Part> parts = request.getParts();log.info("parts={}", parts);for (Part part : parts) { log.info("==== PART ===="); log.info("name={}", part.getName()); Collection<String> headerNames = part.getHeaderNames(); for (String headerName : headerNames) { log.info("header {}: {}", headerName, part.getHeader(headerName)); } //편의 메서드 //content-disposition; filename log.info("submittedFilename={}", part.getSubmittedFileName()); log.info("size={}", part.getSize()); //part body size //데이터 읽기 InputStream inputStream = part.getInputStream(); String body = StreamUtils.copyToString(inputStream, StandardCharsets.UTF_8); log.info("body={}", body); //파일에 저장하기 if (StringUtils.hasText(part.getSubmittedFileName())) { String fullPath = fileDir + part.getSubmittedFileName(); log.info("파일 저장 fullPath={}", fullPath); part.write(fullPath); }} @Slf4j@Controller@RequestMapping("/servlet/v2")public class ServletUploadControllerV2 { @Value("${file.dir}") private String fileDir; @GetMapping("/upload") public String newFile() { return "upload-form"; } @PostMapping("/upload") public String saveFileV1(HttpServletRequest request) throws ServletException, IOException { log.info("request={}", request); String itemName = request.getParameter("itemName"); log.info("itemName={}", itemName); Collection<Part> parts = request.getParts(); log.info("parts={}", parts); for (Part part : parts) { log.info("==== PART ===="); log.info("name={}", part.getName()); Collection<String> headerNames = part.getHeaderNames(); for (String headerName : headerNames) { log.info("header {}: {}", headerName, part.getHeader(headerName)); } //편의 메서드 //content-disposition; filename log.info("submittedFilename={}", part.getSubmittedFileName()); log.info("size={}", part.getSize()); //part body size //데이터 읽기 InputStream inputStream = part.getInputStream(); String body = StreamUtils.copyToString(inputStream, 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 "upload-form"; }}