02.ETC
ETC
λ©μμ§, κ΅μ ν
λ©μμ§ κΈ°λ₯
: λ€μν λ©μμ§λ₯Ό ν κ³³μμ κ΄λ¦¬νλ κΈ°λ₯
messages.properteis
item=μν
item.id=μν ID
item.itemName=μνλͺ
item.price=κ°κ²©
item.quantity=μλ
κ΅μ ν κΈ°λ₯
: λ©μμ§ νμΌμ κ° λλΌλ³λ‘ λ³λλ‘ κ΄λ¦¬νλ κ΅μ ν κΈ°λ₯
messages_en.properties μ κ°μ΄ νμΌλͺ λ§μ§λ§μ μΈμ΄ μ 보 μΆκ°
μ°Ύμ μ μλ κ΅μ ν νμΌμ΄ μμΌλ©΄ messages.properties λ₯Ό κΈ°λ³ΈμΌλ‘ μ¬μ©
messages_en.propertis
item=Item
item.id=Item ID
item.itemName=Item Name
item.price=price
item.quantity=quantity
messages_ko.propertis
item=μν
item.id=μν ID
item.itemName=μνλͺ
item.price=κ°κ²©
item.quantity=μλ
Spring Message Source
SpringBoot λ MessageSource λ₯Ό μλμΌλ‘ μ€νλ§ λΉμΌλ‘ λ±λ‘
Spring μ¬μ© μ ꡬνμ²΄μΈ ResourceBundleMessageSource λ₯Ό λΉμΌλ‘ λ±λ‘
@Bean public MessageSource messageSource() { ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource(); // messages μ§μ μ messages.properties νμΌμ μ½μ΄μ μ¬μ© messageSource.setBasenames("messages", "errors"); messageSource.setDefaultEncoding("utf-8"); return messageSource; }
SpringBoot Message Source μ€μ
application.properties
spring.messages.basename=messages,config.i18n.messages
μ€νλ§ λΆνΈ λ©μμ§ μμ€ κΈ°λ³Έ κ°:
spring.messages.basename=messages
MessageSource λ₯Ό μ€νλ§ λΉ λ±λ‘νμ§ μκ³ , μ€νλ§ λΆνΈ κ΄λ ¨ μ€μ μ νμ§ μμΌλ©΄ messages λΌλ μ΄λ¦μΌλ‘ κΈ°λ³Έ λ±λ‘
λ°λΌμ messages.properties, messages_en.properties .. νμΌλ§ λ±λ‘νλ©΄ μλμΌλ‘ μΈμ
μΆκ° μ΅μ μ Spring-Boot Docs μ°Έκ³
/resources/messages.properties
κ²½λ‘μ Message νμΌ μ μ₯hello=μλ hello.name=μλ {0}
Message Source μ¬μ©
SpringBoot λ MessageSource λ₯Ό μλμΌλ‘ Spring Bean μΌλ‘ λ±λ‘νλ―λ‘ λ°λ‘ μ¬μ© κ°λ₯
MessageSource λ message.properties νμΌ μ 보λ₯Ό κ°μ§κ³ μμ
@Autowired
MessageSource ms;
@Test
void helloMessage() {
// locale μ λ³΄κ° μμΌλ©΄ basename μμ μ€μ ν κΈ°λ³Έ μ΄λ¦ λ©μμ§ νμΌ(messages.properties) μ‘°ν
String result = ms.getMessage("hello", null, null);
assertThat(result).isEqualTo("μλ
");
}
@Test
void notFoundMessageCode() {
// λ©μμ§κ° μλ κ²½μ° NoSuchMessageException λ°μ
assertThatThrownBy(() -> ms.getMessage("no_code", null, null))
.isInstanceOf(NoSuchMessageException.class);
}
@Test
void notFoundMessageCodeDefaultMessage() {
// λ©μμ§κ° μμ΄λ defaultMessage λ₯Ό μ¬μ©νλ©΄ κΈ°λ³Έ λ©μμ§ λ°ν
String result = ms.getMessage("no_code", null, "κΈ°λ³Έ λ©μμ§", null);
assertThat(result).isEqualTo("κΈ°λ³Έ λ©μμ§");
}
@Test
void argumentMessage() {
// λ©μμ§μ {0} λΆλΆμ λ§€κ°λ³μλ₯Ό μ λ¬ν΄μ μΉν
String result = ms.getMessage("hello.name", new Object[]{"Aaron"}, null);
assertThat(result).isEqualTo("μλ
Aaron");
}
Message Source κ΅μ ν μ¬μ©
locale μ 보 κΈ°λ°μΌλ‘ κ΅μ ν νμΌ μ ν
Locale μ΄ en_US μΌ κ²½μ° messages_en_US β messages_en β messages(default) μμ νμ
@Test
void defaultLang() {
// locale μ λ³΄κ° μμΌλ―λ‘ messages μ¬μ©
assertThat(ms.getMessage("hello", null, null)).isEqualTo("μλ
");
// locale μ λ³΄κ° μμ§λ§, message_ko κ° μμΌλ―λ‘ messages μ¬μ©
assertThat(ms.getMessage("hello", null, Locale.KOREA)).isEqualTo("μλ
");
}
@Test
void enLang() {
// locale μ λ³΄κ° Locale.ENGLISH μ΄λ―λ‘ messages_en μ¬μ©
assertThat(ms.getMessage("hello", null, Locale.ENGLISH)).isEqualTo("hello");
}
Web Application Message
λ©μμ§ μ μ©
νμ리νμ λ©μμ§ ννμ
#{...}
λ₯Ό μ¬μ©νλ©΄ μ€νλ§ λ©μμ§λ₯Ό νΈλ¦¬νκ² μ‘°ν κ°λ₯messages.properties
label.item=μν hello.name=μλ {0}
Thymeleaf
<div th:text="#{label.item}"></h2> <p th:text="#{hello.name(${item.itemName})}"></p>
κ΅μ ν μ μ©
μΉ λΈλΌμ°μ μ μΈμ΄ μ€μ κ°μ΄ λ³νλ©΄ μμ²μ Accept-Language μ κ°μ΄ λ³κ²½λκ³ , μ΄ μ 보λ₯Ό Spring μ Locale λ‘ μΈμν΄ μλμΌλ‘ κ΅μ ν μ²λ¦¬
LocaleResolver
Spring μ Locale μ ν λ°©μμ λ³κ²½ν μ μλλ‘ LocaleResolver μΈν°νμ΄μ€ μ 곡
Spring Boot λ μΈμ΄ μ ν μ κΈ°λ³Έμ μΌλ‘ Accept-Language ν€λκ°μ νμ©νλ AcceptHeaderLocaleResolver μ¬μ©
Locale μ ν λ°©μμ λ³κ²½νλ €λ©΄ LocaleResolver ꡬν체λ₯Ό λ³κ²½ν΄μ μΏ ν€λ μΈμ κΈ°λ°μ Locale μ ν κΈ°λ₯ μ¬μ©
Spring Type Converter
μ€νλ§ νμ λ³ν μ μ© μ
HTTP Query String μΌλ‘ μ λ¬λλ λ°μ΄ν°λ λͺ¨λ String Type μ΄μ§λ§, μ€νλ§μ νμ μ λ³νν΄ μ 곡
@RequestParam
@ModelAttribute
@PathVariable
@Value
XML Spring Bean μ 보 λ³ν
View Rendering
...
// @RequestParam @GetMapping("/hello") public String hello(@RequestParam Integer data) {} // @ModelAttribute @GetMapping("/hello") public String hello(@ModelAttribute UserData data) {} class UserData { Integer data; } // @PathVariable @GetMapping("/users/{userId}") public String hello(@PathVariable("data") Integer data) {} // @Value @Value("${api.key}") private String key;
Type Converter
Converter Interface
μ€νλ§μ μ¬μ©μ μ μ νμ λ³νμ΄ νμνλ©΄ 컨λ²ν° μΈν°νμ΄μ€λ₯Ό ꡬνν΄μ λ±λ‘ν΄ λ³΄μ.
package org.springframework.core.convert.converter;
public interface Converter<S, T> {
T convert(S source);
}
ex. 컨λ²ν° μΈν°νμ΄μ€ ꡬν
public class StringToIntegerConverter implements Converter<String, Integer> {
@Override
public Integer convert(String source) {
return Integer.valueOf(source);
}
}
...
@Test
void stringToInteger() {
StringToIntegerConverter converter = new StringToIntegerConverter();
Integer result = converter.convert("10");
assertThat(result).isEqualTo(10);
}
μ€νλ§μ μ©λμ λ°λΌ λ€μν λ°©μμ νμ 컨λ²ν° μ 곡
Converter
: κΈ°λ³Έ νμ 컨λ²ν°ConverterFactory
: μ 체 ν΄λμ€ κ³μΈ΅ κ΅¬μ‘°κ° νμν κ²½μ°GenericConverter
: μ κ΅ν ꡬν, λμ νλμ μ λ Έν μ΄μ μ 보 μ¬μ© κ°λ₯ConditionalGenericConverter
: νΉμ μ‘°κ±΄μ΄ μ°ΈμΈ κ²½μ°μλ§ μ€νκ·Έλ°μ λ¬Έμ, μ«μ, boolean, Enum λ± μΌλ°μ μΈ νμ μ λν λλΆλΆμ 컨λ²ν°λ₯Ό κΈ°λ³ΈμΌλ‘ μ 곡
ConversionService
κ°λ³ 컨λ²ν°λ₯Ό λͺ¨μλκ³ , κ·Έκ²λ€μ λ¬Άμ΄μ νΈλ¦¬νκ² μ¬μ©ν μ μλ κΈ°λ₯
μ€νλ§μ @RequestParam κ°μ κ³³ λ΄λΆμμ ConversionService λ₯Ό μ¬μ©ν΄μ νμ μ λ³ν
ConversionService interface
Converting κ°λ₯ μ¬λΆμ κΈ°λ₯ μ 곡
package org.springframework.core.convert;
import org.springframework.lang.Nullable;
public interface ConversionService {
boolean canConvert(@Nullable Class<?> sourceType, Class<?> targetType);
boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
@Nullable
<T> T convert(@Nullable Object source, Class<T> targetType);
@Nullable
Object convert(@Nullable Object source, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType);
}
DefaultConversionService
ConversionService μΈν°νμ΄μ€μ ꡬν체(컨λ²ν°λ₯Ό λ±λ‘νλ κΈ°λ₯λ μ 곡)
μ¬μ© μ΄μ μ
ConversionService
μ λ±λ‘ μ΄μ μConverterRegistry
λ‘ λΆλ¦¬λμ΄ κ΅¬νμΈν°νμ΄μ€ λΆλ¦¬ μμΉ μ μ©(
ISP
-Interface Segregation Principal)μΈν°νμ΄μ€ λΆλ¦¬λ₯Ό ν΅ν΄ 컨λ²ν°λ₯Ό μ¬μ©νλ ν΄λΌμ΄μΈνΈμ 컨λ²ν°λ₯Ό λ±λ‘νκ³ κ΄λ¦¬νλ ν΄λΌμ΄μΈνΈμ κ΄μ¬μ¬λ₯Ό λͺ ννκ² λΆλ¦¬
.
νμ 컨λ²ν°λ€μ 컨λ²μ μλΉμ€ λ΄λΆμ μ¨μ΄μ μ 곡λλ―λ‘, ν΄λΌμ΄μΈνΈλ νμ 컨λ²ν°λ₯Ό λͺ°λΌλ 무κ΄
νμ λ³νμ μνλ ν΄λΌμ΄μΈνΈμ κ²½μ° μ»¨λ²μ μλΉμ€ μΈν°νμ΄μ€μλ§ μμ‘΄
컨λ²μ μλΉμ€λ₯Ό λ±λ‘νλ λΆλΆκ³Ό μ¬μ©νλ λΆλΆμ λΆλ¦¬νκ³ μμ‘΄κ΄κ³ μ£Όμ μ μ¬μ©
@Test
void conversionService() {
// Converter λ±λ‘
DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(new StringToIntegerConverter());
conversionService.addConverter(new IntegerToStringConverter());
// ConverterService μ¬μ©
assertThat(conversionService.convert("10", Integer.class)).isEqualTo(10);
assertThat(conversionService.convert(10, String.class)).isEqualTo("10");
}
Apply Converter in Spring π
μ€νλ§μ λ΄λΆμμ ConversionService μ 곡
WebMvcConfigurer κ° μ 곡νλ
addFormatters()
λ₯Ό μ¬μ©ν΄μ 컨λ²ν° λ±λ‘@RequestParam μ κ²½μ° RequestParamMethodArgumentResolver μμ ConversionService λ₯Ό μ¬μ©ν΄μ νμ μ λ³ν
WebConfig.java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToIntegerConverter());
registry.addConverter(new IntegerToStringConverter());
}
}
...
@GetMapping("/hello-v2")
public String helloV2(@RequestParam Integer data) {
return "ok";
}
Apply Converter in View Template
νμ리νλ λ λλ§ μ 컨λ²ν°λ₯Ό μ μ©ν΄μ λ λλ§ νλ λ°©λ²μ νΈλ¦¬νκ² μ§μ
View Template
Controller.java
@GetMapping("/view")
public String converterView(Model model) {
model.addAttribute("number", 10000);
return "view";
}
view.html
λ³μ ννμ :
${...}
컨λ²μ μλΉμ€ μ μ© :
${{...}}
<li>${number}: <span th:text="${number}" ></span></li>
<li>${{number}}: <span th:text="${{number}}" ></span></li>
Form
Controller.java
@ModelAttribute λ΄λΆμμ ConversionService λμ
@GetMapping("/converter/edit")
public String converterForm(Model model) {
IpPort ipPort = new IpPort("127.0.0.1", 8080);
Form form = new Form(ipPort);
model.addAttribute("form", form);
return "form";
}
@PostMapping("/converter/edit")
public String converterEdit(@ModelAttribute Form form, Model model) {
IpPort ipPort = form.getIpPort();
model.addAttribute("ipPort", ipPort);
return "view";
}
form.html
th:field λ Converter κΉμ§ μλ μ μ©
th:value λ 보μ¬μ£Όλ μ©λ
<form th:object="${form}" th:method="post">
th:field <input type="text" th:field="*{ipPort}" /><br />
th:value <input type="text" th:value="*{ipPort}" /><br />
<input type="submit" />
</form>
Formatter
κ°μ²΄λ₯Ό νΉμ ν ν¬λ©§μ λ§μΆμ΄ λ¬Έμλ‘ μΆλ ₯νκ±°λ, κ·Έ λ°λμ μν μ νλ κ²μ νΉνλ κΈ°λ₯
Converter
: λ²μ©(κ°μ²΄ β κ°μ²΄)μ μ¬μ©Formatter
: λ¬Έμ(κ°μ²΄ β λ¬Έμ, λ¬Έμ β κ°μ²΄, νμ§ν)μ νΉννΉλ³ν Converter..
Formatter Interface
public interface Printer<T> { // κ°μ²΄ β λ¬Έμ
String print(T object, Locale locale);
}
public interface Parser<T> { // λ¬Έμ β κ°μ²΄
T parse(String text, Locale locale) throws ParseException;
}
public interface Formatter<T> extends Printer<T>, Parser<T> {
}
implements Formatter
public class NumberFormatter implements Formatter<Number> {
@Override
public Number parse(String text, Locale locale) throws ParseException {
NumberFormat format = NumberFormat.getInstance(locale);
return format.parse(text);
}
@Override
public String print(Number object, Locale locale) {
return NumberFormat.getInstance(locale).format(object);
}
}
...
class MyNumberFormatterTest {
MyNumberFormatter formatter = new MyNumberFormatter();
@Test
void parse() throws ParseException {
Number result = formatter.parse("1,000", Locale.KOREA);
assertThat(result).isEqualTo(1000L);
}
@Test
void print() {
String result = formatter.print(1000, Locale.KOREA);
assertThat(result).isEqualTo("1,000");
}
}
μ€νλ§μ μ©λμ λ°λΌ λ€μν λ°©μμ ν¬λ§·ν° μ 곡
AnnotationFormatterFactory: νλμ νμ μ΄λ μ λ Έν μ΄μ μ 보λ₯Ό νμ©ν μ μλ ν¬λ§·ν°
FormattingConversionService
ConverstionService μλ 컨λ²ν°λ§ λ±λ‘ κ°λ₯νκ³ , ν¬λ§·ν°λ λ±λ‘ λΆκ°
ν¬λ§·ν° λ±λ‘μ μ§μνλ
FormattingConversionService
λ₯Ό μ¬μ©νμ¬ ν¬λ§·ν°λ₯Ό μΆκ°ν΄ 보μ.λ΄λΆμμ μ΄λν° ν¨ν΄μ μ¬μ©ν΄μ Formatter κ° Converter μ²λΌ λμνλλ‘ μ§μ
DefaultFormattingConversionService
λ FormattingConversionService λ₯Ό μμλ°μ κΈ°λ³Έμ μΈ ν΅ν, μ«μ κ΄λ ¨ κΈ°λ³Έ ν¬λ§·ν°λ₯Ό μΆκ° μ 곡ConversionService κ΄λ ¨ κΈ°λ₯μ μμλ°μΌλ―λ‘ Converter, Formatter λͺ¨λ λ±λ‘ κ°λ₯
μ€νλ§ λΆνΈλ DefaultFormattingConversionService λ₯Ό μμ λ°μ
WebConversionService
λ₯Ό λ΄λΆμμ μ¬μ©
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
// 컨λ²ν° λ±λ‘
conversionService.addConverter(new StringToIpPortConverter());
conversionService.addConverter(new IpPortToStringConverter());
// ν¬λ§·ν° λ±λ‘
conversionService.addFormatter(new MyNumberFormatter());
// 컨λ²ν° μ¬μ©
IpPort ipPort = conversionService.convert("127.0.0.1:8080", IpPort.class);
assertThat(ipPort).isEqualTo(new IpPort("127.0.0.1", 8080));
// ν¬λ§·ν° μ¬μ©
assertThat(conversionService.convert(1000, String.class)).isEqualTo("1,000");
assertThat(conversionService.convert("1,000", Long.class)).isEqualTo(1000L);
Apply Formatter in Spring π
κΈ°λ₯μ΄ κ²ΉμΉ κ²½μ°(Source-type, Target-type λμΌ) Converter μ°μ
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToIpPortConverter());
registry.addConverter(new IpPortToStringConverter());
registry.addFormatter(new MyNumberFormatter());
}
}
Annotation driven Formatting
μ€νλ§μ μλ°μμ κΈ°λ³ΈμΌλ‘ μ 곡νλ νμ λ€μ λν΄ μλ§μ ν¬λ§·ν°λ₯Ό κΈ°λ³ΈμΌλ‘ μ 곡
κ°μ²΄μ κ° νλλ§λ€ λ€λ₯Έ νμμ ν¬λ§·μ μ§μ νλ μ΄λ €μμ ν΄κ²°νκΈ° μν΄ μ λ Έν μ΄μ κΈ°λ° νμ μ§μ ν¬λ§·ν° μ 곡
@NumberFormat
: μ«μ κ΄λ ¨ νμ μ§μ ν¬λ§·ν° μ¬μ©NumberFormatAnnotationFormatterFactory
@DateTimeFormat
: λ μ§ κ΄λ ¨ νμ μ§μ ν¬λ§·ν° μ¬μ©Jsr310DateTimeFormatAnnotationFormatterFactory
@Getter @Setter static class Form { @NumberFormat(pattern = "###,###") private Integer number; @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") private LocalDateTime localDateTime; }
μ°Έκ³ ,
HttpMessageConverter μλ Convetion Service κ° μ μ©λμ§ μμ
JSON μ κ°μ²΄λ‘ λ³ννλ HttpMessageConverter λ λ΄λΆμμ Jackson κ°μ λΌμ΄λΈλ¬λ¦¬λ₯Ό μ¬μ©
λ°λΌμ, JSON κ²°κ³Όλ‘ λ§λ€μ΄μ§λ μ«μλ λ μ§ ν¬λ§·μ λ³κ²½νκ³ μΆμΌλ©΄ ν΄λΉ λΌμ΄λΈλ¬λ¦¬κ° μ 곡νλ μ€μ μ ν΅ν΄μ ν¬λ§·μ μ§μ
File Upload
μ μ‘ λ°©μ
κΈ°λ³Έμ μΈ HTML Form μ μ‘ λ°©μ
application/x-www-form-urlencoded
HTML Form
<form action="/save" method="post"> <inpout type="text" name="username" /> <inpout type="text" name="age" /> <button type="submit">μ μ‘</button> </form>
HTTP Message
HTTP/1.1 200 OK POST /save HTTP/1.1 Host: localhost:8080 Content-Type: application/x-www-form-urlencoded username=kim&age=20
Form λ΄μ©κ³Ό μ¬λ¬ νμΌμ ν¨κ» μ μ‘νλ HTML Form μ μ‘ λ°©μ
multipart/form-data
HTML Form
form tag μ enctype="multipart/form-data" μ§μ
<form action="/save" method="post" enctype="multipart/form-data"> <inpout type="text" name="username" /> <inpout type="text" name="age" /> <inpout type="file" name="file1" /> <button type="submit">μ μ‘</button> </form>
HTTP Message
κ°κ°μ μ μ‘ νλͺ©μ΄ ꡬλΆ
Content-Disposition λΌλ νλͺ©λ³ ν€λμ λΆκ° μ λ³΄κ° λΆλ¦¬
HTTP/1.1 200 OK POST /save HTTP/1.1 Host: localhost:8080 Content-Type: multipart/form-data; boundary=----XXX Content-Length: 10457 ----XXX Content-Disposition: form-data; name="username" Kim ----XXX Content-Disposition: form-data; name="age" 20 ----XXX Content-Disposition: form-data; name="file1"; filename="sample.jpg" Content-Type: image/png 102941as9d86f7aa9807sd6fas987df6... ----XXX--
Servlet File Upload
Multipart κ΄λ ¨ μ€μ
# HTTP μμ² λ©μμ§ νμΈ
logging.level.org.apache.coyote.http11=debug
# νμΌ μ
λ‘λ κ²½λ‘ μ€μ
file.dir=C:/Users/Aaron/file/
# μ
λ‘λ μ¬μ΄μ¦ μ ν (μ¬μ΄μ¦ μ΄κ³Ό μ SizeLimitExceededException μμΈ λ°μ)
# max-file-size : νμΌ νλ μ¬μ΄μ¦ (default > 1MB)
# max-request-size : μ¬λ¬ νμΌ μμ²μ κ²½μ° μ 체 μ¬μ΄μ¦ (default > 10MB)
spring.servlet.multipart.max-file-size=1MB
spring.servlet.multipart.max-request-size=10MB
# Multipart λ°μ΄μ² μ²λ¦¬ μ¬λΆ (default > true)
spring.servlet.multipart.enabled=true
multipart.enabled μ΅μ μ΄ μΌμ Έ μλ€λ©΄, Spring
DispatcherServlet
μμMultipartResolver
μ€νmultipart μμ²μΈ κ²½μ° Servlet Container κ° μ λ¬νλ
HttpServletRequest
λ₯ΌMultipartHttpServletRequest
λ‘ λ³νν΄μ λ°νSpring μ΄ μ 곡νλ κΈ°λ³Έ
MultipartResolver
λMultipartHttpServletRequest
Interface λ₯Ό ꡬννStandardMultipartHttpServletRequest
λ₯Ό λ°ν
ServletUploadController.java
@Slf4j
@Controller
@RequestMapping("/servlet/")
public class ServletUploadControllerV2 {
/**
* properties μ€μ κ° μ£Όμ
*/
@Value("${file.dir}")
private String fileDir;
@GetMapping("/upload")
public String newFile() {
return "upload-form";
}
@PostMapping("/upload")
public String saveFile(HttpServletRequest request) throws ServletException, IOException {
log.info("request={}", request);
String itemName = request.getParameter("itemName");
log.info("itemName={}", itemName);
/**
* Multipart νμμ μ μ‘ λ°μ΄ν°λ₯Ό κ° Part λ‘ λλμ΄ μ μ‘
*/
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: form-data; name="file"; filename="image.png"
//Content-Type: image/png
log.info("submittedFileName={}", part.getSubmittedFileName()); // ν΄λΌμ΄μΈνΈκ° μ λ¬ν νμΌλͺ
log.info("size={}", part.getSize()); //part body size
//λ°μ΄ν° μ½κΈ°
InputStream inputStream = part.getInputStream(); // Partμ μ μ‘ λ°μ΄ν° μ½κΈ°
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); // Partλ₯Ό ν΅ν΄ μ μ‘λ λ°μ΄ν°λ₯Ό μ μ₯
}
}
return "upload-form";
}
}
request=org.springframework.web.multipart.support.StandardMultipartHttpServletRequest@2b82974a
itemName=Spring
parts=[org.apache.catalina.core.ApplicationPart@367a8c9f, org.apache.catalina.core.ApplicationPart@33180a33]
==== PART ====
name=itemName
header content-disposition: form-data; name="itemName"
submittedFileName=null
size=6
body=Spring
==== PART ====
name=file
header content-disposition: form-data; name="file"; filename="image.png"
header content-type: image/png
submittedFileName=image.png
size=191492
body=οΏ½PNG
...
...
Spring File Upload π
μ€νλ§μ
MultipartFile
Interface λ‘ Multipart File μ λ§€μ° νΈλ¦¬νκ² μ§μ
@PostMapping("/upload")
public String saveFile(@RequestParam String itemName,
@RequestParam MultipartFile file, HttpServletRequest request) throws IOException {
if (!file.isEmpty()) {
String fullPath = fileDir + file.getOriginalFilename(); //μ
λ‘λ νμΌ λͺ
log.info("νμΌ μ μ₯ fullPath={}", fullPath);
file.transferTo(new File(fullPath)); //νμΌ μ μ₯
}
return "upload-form";
}
File Upload And Download
Last updated