I'm currently trying to send data from a form (name, password, image) etc. I'm using Spring Boot, JPA, Thymeleaf and MultipartFile, but I'm having the following error:
Failed to convert property value of type
org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile
to required type byte[] for property header;
nested exception is java.lang.IllegalArgumentException:
Cannot convert value of type org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile
to required type byte for property header[0]: PropertyEditor [org.springframework.beans.propertyeditors.CustomNumberEditor]
returned inappropriate value of type org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile
I've searched on google before posting my question here, but couldn't find anything that managed to help me out. At first, I thought there was something wrong with MultipartFile, but decided to get rid of BindingResult (I was using it to display errors in my thymeleaf template, such as password not matching the regex or some fields not having enough characters etc) and it fixed the problem. The picture was successfully uploaded to the database.
However, I'd like to keep BindingResult and make it work with MultipartFile.
Here's my code.
My entity (Config) :
@Entity
@Table(name = "config")
public class Config {
@Id
@GeneratedValue
private int id;
@NotNull
@Size(min=4, max=50, message="le nom du restaurant doit contenir au moins 4 caractères")
private String restaurantName;
@NotNull
private String url;
@Pattern(regexp="^(?=.{8,}$)(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).*$", flags = Flag.UNICODE_CASE,
message="Le mot de passe doit contenir au moins 8 caratères dont une majuscule, une minuscule et 1 chiffre")
private String password;
private String linkColor;
private String menuColor;
private String buttonColor;
@Column(name = "header")
private byte[] header;
public String getRestaurantName() {
return restaurantName;
}
public void setRestaurantName(String restaurantName) {
this.restaurantName = restaurantName;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getLinkColor() {
return linkColor;
}
public void setLinkColor(String linkColor) {
this.linkColor = linkColor;
}
public String getMenuColor() {
return menuColor;
}
public void setMenuColor(String menuColor) {
this.menuColor = menuColor;
}
public String getButtonColor() {
return buttonColor;
}
public void setButtonColor(String buttonColor) {
this.buttonColor = buttonColor;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public byte[] getHeader() {
return header;
}
public void setHeader(byte[] header) {
this.header = header;
}
}
My ConfigService :
@Service
public class ConfigService {
@Autowired
private ConfigRepository repo;
public Config getConfigById(int id) {
return repo.findById(id).orElse(null);
}
public Config getConfigByRestaurantName(String name) {
return repo.findByRestaurantName(name);
}
public String deleteConfig(int id) {
repo.deleteById(id);
return "Config has been deleted!";
}
public long configAlreadyExist() {
return repo.count();
}
public List<Config> getAll() {
return repo.findAll();
}
public Config saveConfig(Config config) {
return repo.save(config);
}
}
ConfigController :
@PostMapping("/config/add")
public String addConfig(@Valid @ModelAttribute("config") Config config, BindingResult bindingResult, @RequestParam(value="header", required=false) MultipartFile file) throws IOException {
if(bindingResult.hasErrors()) {
return "config";
}
else {
config.setHeader(file.getBytes());
service.saveConfig(config);
return "redirect:/config/confirm";
}
}
My Form (I'm using thymeleaf) :
<form action="#" th:action="@{/config/add}" th:object="${config}" method="post" enctype="multipart/form-data">
<div class="form-group">
<label for="restaurant_name">Nom du restaurant</label>
<input type="text" th:field="*{restaurantName}"/>
<div th:if="${#fields.hasErrors('restaurantName')}" th:errors="*{restaurantName}" class="error">Restaurant Name error</div>
</div>
<div class="form-group">
<label for="url">URL du site</label>
<input type="text" class="form-control" id="url" th:field="*{url}">
</div>
<div class="form-group">
<label for="link_color">Couleur pour les liens</label>
<select class="form-control" th:field="*{linkColor}">
<option value="blue">Bleu</option>
<option value="red">Rouge</option>
<option value="yellow">Jaune</option>
<option value="green">Vert</option>
<option value="grey">Gris</option>
</select>
</div>
<div class="form-group">
<label for="menu_color">Couleur pour les menus</label>
<select class="form-control" th:field="*{menuColor}">
<option value="blue">Bleu</option>
<option value="red">Rouge</option>
<option value="yellow">Jaune</option>
<option value="green">Vert</option>
<option value="grey">Gris</option>
</select>
</div>
<div class="form-group">
<label for="button_color">Couleur pour les boutons</label>
<select class="form-control" th:field="*{buttonColor}">
<option value="blue">Bleu</option>
<option value="red">Rouge</option>
<option value="yellow">Jaune</option>
<option value="green">Vert</option>
<option value="grey">Gris</option>
</select>
</div>
<div class="form-group">
<label for="password">Password</label>
<input type="password" th:field="*{password}"/>
</div>
<div th:if="${#fields.hasErrors('password')}" th:errors="*{password}" class="error">Password error</div>
<div class="form-group">
<label for="header">Header file</label>
<input type="file" class="form-control" id="header" th:field="*{header}">
<div th:if="${#fields.hasErrors('header')}" th:errors="*{header}" class="error">Header error</div>
</div>
<button type="submit" class="btn btn-primary">Submit</button>
<div>
<p th:text="${error}" id="error"></p>
</div>
</form>
From what I understand, in my entity (config), header is a byte[] (longblob) but since MultifilePart is handling my file in a different format, one that doesn't match the byte[] in my entity, that's what is causing the error.
Is there any easy workaround to solve this please?
Thanks!
EDIT : I found a temporary workaround by using @InitBinder
@InitBinder
public void setAllowedFields(WebDataBinder dataBinder) {
dataBinder.setDisallowedFields("header");
}
I'm still interested to know if there's another way to deal with this (without disable the field) so I can still check if the file is a png, jpg etc (to add more security).