无法在 Spring 中将 MultipartFile 转换为 Blob
Cannot convert MultipartFile into Blob in Spring
我正在尝试将上传的文件保存为 MySql 记录中的 Blob。我是 Spring 的新手。当我上传文件后准备保存记录时,当我的 POST 方法 updateCandidate()
执行时,我得到这个异常:
Field error in object 'candidateForm' on field 'cv': rejected value [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@59c09df6]; codes [typeMismatch.candidateForm.cv,typeMismatch.cv,typeMismatch.java.sql.Blob,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [candidateForm.cv,cv]; arguments []; default message [cv]]; default message [Failed to convert property value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'java.sql.Blob' for property 'cv'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'java.sql.Blob' for property 'cv': no matching editors or conversion strategy found]
出了什么问题?如何解决?
我的实体:
import java.sql.Blob;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Lob;
@Entity
public class Candidate {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(unique = true)
private String ssn;
private String name;
private String surname;
private String technology;
private String media;
@Lob
private Blob cv;
private boolean activeCV;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getSsn() {
return ssn;
}
public void setSsn(String ssn) {
this.ssn = ssn;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getTechnology() {
return technology;
}
public void setTechnology(String technology) {
this.technology = technology;
}
public String getMedia() {
return media;
}
public void setMedia(String media) {
this.media = media;
}
public Blob getCv() {
return cv;
}
public void setCv(Blob cv) {
this.cv = cv;
}
public boolean isActiveCV() {
return activeCV;
}
public void setActiveCV(boolean activeCV) {
this.activeCV = activeCV;
}
}
为我服务:
@Autowired
private CandidateRepository repository;
...
public Optional<Candidate> getCandidate(Long id){
return repository.findById(id);
}
public void addOrUpdateCandidate(Candidate candidate) {
repository.save(candidate);
}
在我的控制器中:
@Controller
@RequestMapping("/candidates")
public class CandidateController {
@Autowired
private EntityManagerFactory emf;
@Autowired
private CandidateService service;
...
@GetMapping("/updateCandidate/{id}")
public String showUpdateUserForm(@PathVariable("id") Long id, Model model) {
Candidate candidate = service.getCandidate(id).get();
model.addAttribute("candidateForm", candidate);
return "updateCandidateForm";
}
@PostMapping("/updateCandidate/updateCandidateResult")
public String updateCandidate(@ModelAttribute("candidateForm") Candidate candidate, @RequestParam("cv") MultipartFile file) throws IOException {
InputStream iStream = file.getInputStream();
long size = file.getSize();
Session session = emf.unwrap(Session.class);
Blob cv = Hibernate.getLobCreator(session).createBlob(iStream, size);
candidate.setCv(cv);
service.addOrUpdateCandidate(candidate);
return "updateCandidateResult";
}
}
我的updateCandidateForm.jsp:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<html>
<body>
<form:form method="POST" action="updateCandidateResult" modelAttribute="candidateForm" enctype="multipart/form-data">
<form:hidden path="id"/>
<table>
<tr>
<td><form:label path="name">Name</form:label></td>
<td><form:input path="name"/></td>
</tr>
<tr>
<td><form:label path="surname">Surname</form:label></td>
<td><form:input path="surname"/></td>
</tr>
<tr>
<td><form:label path="ssn">SSN</form:label></td>
<td><form:input path="ssn"/></td>
</tr>
<tr>
<td><form:label path="technology">Known Technology</form:label></td>
<td><form:input path="technology"/></td>
</tr>
<tr>
<td><form:label path="media">Found us on</form:label></td>
<td><form:input path="media"/></td>
</tr>
<tr>
<td><form:label path="cv">Select a cv</form:label></td>
<td><input type="file" name="cv" /></td>
</tr>
<tr>
<td><form:label path="activeCV">Active CV</form:label></td>
<td><form:checkbox path="activeCV" /></td>
</tr>
<tr>
<td><input type="submit" value="Submit"/></td>
</tr>
</table>
</form:form>
</body>
在我的 POM 中:
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
编辑 1(问题):
是否有一种方法可以阻止 Spring 尝试将 MultipartFile 转换为 "Submit-time" 处的 Blob,从而更快地触发此操作,让 POST 方法已经管理了 Candidate(已经设置了 Blob 字段) ) 对象?
编辑 2:
正如 JB Nizet 所建议的,我尝试使用支持 POJO,它具有 MultipartFile 类型的字段 CV,通过表单(文本字段 + 文件字段)临时存储我 post 的内容,但我没有得到那个异常了,因为在 "Submit-time" 处填充的对象具有上传文件类型的 cv 字段:
import org.springframework.web.multipart.MultipartFile;
public class CandidatePOJO {
private Long id;
private String ssn;
private String name;
private String surname;
private String technology;
private String media;
private MultipartFile cv;
private boolean activeCV;
@Override
public String toString() {
return "CandidatePOJO [id=" + id + ", ssn=" + ssn + ", name=" + name + ", surname=" + surname + ", technology="
+ technology + ", media=" + media + ", cv=" + cv + ", activeCV=" + activeCV + "]";
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getSsn() {
return ssn;
}
public void setSsn(String ssn) {
this.ssn = ssn;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getTechnology() {
return technology;
}
public void setTechnology(String technology) {
this.technology = technology;
}
public String getMedia() {
return media;
}
public void setMedia(String media) {
this.media = media;
}
public MultipartFile getCv() {
return cv;
}
public void setCv(MultipartFile cv) {
this.cv = cv;
}
public boolean isActiveCV() {
return activeCV;
}
public void setActiveCV(boolean activeCV) {
this.activeCV = activeCV;
}
}
controller中,现在我首先关心的是pojo能否正确实例化,所以我的GET-POST对是:
@GetMapping("/updateCandidate/{id}")
public String showUpdateUserForm(@PathVariable("id") Long id, Model model) {
CandidatePOJO candidatePOJO = new CandidatePOJO();
candidatePOJO.setId(id);
model.addAttribute("candidateForm", candidatePOJO);
return "updateCandidateForm";
}
@PostMapping("/updateCandidate/updateCandidateResult")
public String updateCandidate(@ModelAttribute("candidateForm") CandidatePOJO candidatePOJO) {
System.out.println("CANDIDATE POJO");
System.out.println(candidatePOJO.toString()); // here I notice id = null
/* MultipartFile to Blob conversion */
// MultipartFile file = candidatePOJO.getCv();
// InputStream iStream = file.getInputStream();
// long size = file.getSize();
// Session session = emf.unwrap(Session.class);
// Blob cv = Hibernate.getLobCreator(session).createBlob(iStream, size);
/* instantiating the entity object to be freezed in db */
// Candidate candidate = new Candidate();
// set all data from candidatePOJO..
// candidate.setCv(cv);
// service.addOrUpdateCandidate(candidate);
return "updateCandidateResult";
}
我得到了一个未设置 ID 的 CandidatePOJO 对象。我无法将 id 从 GET 传递到 POST。有谁知道什么是错的吗?
编辑 3:
很多天后,我选择了支持 POJO 的解决方案,令人难以置信的是,我注意到 ID 从 GET 方法传递到 POST 方法(我没有改变任何东西,我刚刚执行了经典的 Maven 项目就像我 post 编辑我的问题时所做的那样清洁)。不幸的是,我现在面临另一个异常(当然,在恢复之前在 POST 方法中注释的代码之后):
javax.persistence.PersistenceException: Hibernate cannot unwrap EntityManagerFactory as 'org.hibernate.Session'
如何解决?
编辑 4:
上面的异常解决了替换:
Session session = emf.unwrap(Session.class);
与:
EntityManager em = emf.createEntityManager();
Session session = (Session) em.getDelegate();
BY USING SUPPORT POJO,我终于解决了。确实我已经通过代码解决了。 post 中的代码是正确的 (EDIT 2)。当我经常进行Maven项目清理时,并没有发现错误。我在最后几个小时执行了它,令人难以置信的是我的代码有效。我猜不出魔法 :D 我不知道到底发生了什么。通过代码,我用两种不同的方式解决了。第一:
@Autowired
private EntityManagerFactory emf;
// ........
@GetMapping("/updateCandidate/{id}")
public String showUpdateUserForm(@PathVariable("id") Long id, Model model) {
Candidate candidate = service.getCandidate(id).get();
CandidatePOJO candidatePOJO = new CandidatePOJO();
candidatePOJO.setId(id);
candidatePOJO.setName(candidate.getName());
candidatePOJO.setSurname(candidate.getSurname());
candidatePOJO.setSsn(candidate.getSsn());
candidatePOJO.setMedia(candidate.getMedia());
candidatePOJO.setTechnology(candidate.getTechnology());
candidatePOJO.setActiveCV(candidate.isActiveCV());
model.addAttribute("candidateForm", candidatePOJO);
return "updateCandidateForm";
}
@PostMapping("/updateCandidate/updateCandidateResult")
public String updateCandidate(@ModelAttribute("candidateForm") CandidatePOJO candidatePOJO) throws IOException {
MultipartFile file = candidatePOJO.getCv();
InputStream iStream = file.getInputStream();
long size = file.getSize();
EntityManager em = emf.createEntityManager();
Session session = (Session) em.getDelegate();
Blob cv = Hibernate.getLobCreator(session).createBlob(iStream, size);
Candidate candidate = new Candidate();
candidate.setId(candidatePOJO.getId());
candidate.setName(candidatePOJO.getName());
candidate.setSurname(candidatePOJO.getSurname());
candidate.setSsn(candidatePOJO.getSsn());
candidate.setMedia(candidatePOJO.getMedia());
candidate.setTechnology(candidatePOJO.getTechnology());
candidate.setActiveCV(candidatePOJO.isActiveCV());
candidate.setCv(cv);
service.addOrUpdateCandidate(candidate);
return "updateCandidateResult";
}
第二个(同一个GET):
@PostMapping("/updateCandidate/updateCandidateResult")
public String updateCandidate(@ModelAttribute("candidateForm") CandidatePOJO candidatePOJO) throws IOException, SerialException, SQLException {
MultipartFile file = candidatePOJO.getCv();
Blob cv = new SerialBlob(file.getBytes());
Candidate candidate = new Candidate();
candidate.setId(candidatePOJO.getId());
candidate.setName(candidatePOJO.getName());
candidate.setSurname(candidatePOJO.getSurname());
candidate.setSsn(candidatePOJO.getSsn());
candidate.setMedia(candidatePOJO.getMedia());
candidate.setTechnology(candidatePOJO.getTechnology());
candidate.setActiveCV(candidatePOJO.isActiveCV());
candidate.setCv(cv);
service.addOrUpdateCandidate(candidate);
return "updateCandidateResult";
}
作为 Spring 的新手,我还不知道其中的区别。我将不胜感激解释。此外,我迟早要修改此代码,以摆脱对 POJO 的支持并仅使用实体对象:如果有人能解决我最初的问题,我将永远感激不已!
我正在尝试将上传的文件保存为 MySql 记录中的 Blob。我是 Spring 的新手。当我上传文件后准备保存记录时,当我的 POST 方法 updateCandidate()
执行时,我得到这个异常:
Field error in object 'candidateForm' on field 'cv': rejected value [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@59c09df6]; codes [typeMismatch.candidateForm.cv,typeMismatch.cv,typeMismatch.java.sql.Blob,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [candidateForm.cv,cv]; arguments []; default message [cv]]; default message [Failed to convert property value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'java.sql.Blob' for property 'cv'; nested exception is java.lang.IllegalStateException: Cannot convert value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'java.sql.Blob' for property 'cv': no matching editors or conversion strategy found]
出了什么问题?如何解决?
我的实体:
import java.sql.Blob;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Lob;
@Entity
public class Candidate {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Column(unique = true)
private String ssn;
private String name;
private String surname;
private String technology;
private String media;
@Lob
private Blob cv;
private boolean activeCV;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getSsn() {
return ssn;
}
public void setSsn(String ssn) {
this.ssn = ssn;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getTechnology() {
return technology;
}
public void setTechnology(String technology) {
this.technology = technology;
}
public String getMedia() {
return media;
}
public void setMedia(String media) {
this.media = media;
}
public Blob getCv() {
return cv;
}
public void setCv(Blob cv) {
this.cv = cv;
}
public boolean isActiveCV() {
return activeCV;
}
public void setActiveCV(boolean activeCV) {
this.activeCV = activeCV;
}
}
为我服务:
@Autowired
private CandidateRepository repository;
...
public Optional<Candidate> getCandidate(Long id){
return repository.findById(id);
}
public void addOrUpdateCandidate(Candidate candidate) {
repository.save(candidate);
}
在我的控制器中:
@Controller
@RequestMapping("/candidates")
public class CandidateController {
@Autowired
private EntityManagerFactory emf;
@Autowired
private CandidateService service;
...
@GetMapping("/updateCandidate/{id}")
public String showUpdateUserForm(@PathVariable("id") Long id, Model model) {
Candidate candidate = service.getCandidate(id).get();
model.addAttribute("candidateForm", candidate);
return "updateCandidateForm";
}
@PostMapping("/updateCandidate/updateCandidateResult")
public String updateCandidate(@ModelAttribute("candidateForm") Candidate candidate, @RequestParam("cv") MultipartFile file) throws IOException {
InputStream iStream = file.getInputStream();
long size = file.getSize();
Session session = emf.unwrap(Session.class);
Blob cv = Hibernate.getLobCreator(session).createBlob(iStream, size);
candidate.setCv(cv);
service.addOrUpdateCandidate(candidate);
return "updateCandidateResult";
}
}
我的updateCandidateForm.jsp:
<%@ page language="java" contentType="text/html; charset=ISO-8859-1" pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form"%>
<html>
<body>
<form:form method="POST" action="updateCandidateResult" modelAttribute="candidateForm" enctype="multipart/form-data">
<form:hidden path="id"/>
<table>
<tr>
<td><form:label path="name">Name</form:label></td>
<td><form:input path="name"/></td>
</tr>
<tr>
<td><form:label path="surname">Surname</form:label></td>
<td><form:input path="surname"/></td>
</tr>
<tr>
<td><form:label path="ssn">SSN</form:label></td>
<td><form:input path="ssn"/></td>
</tr>
<tr>
<td><form:label path="technology">Known Technology</form:label></td>
<td><form:input path="technology"/></td>
</tr>
<tr>
<td><form:label path="media">Found us on</form:label></td>
<td><form:input path="media"/></td>
</tr>
<tr>
<td><form:label path="cv">Select a cv</form:label></td>
<td><input type="file" name="cv" /></td>
</tr>
<tr>
<td><form:label path="activeCV">Active CV</form:label></td>
<td><form:checkbox path="activeCV" /></td>
</tr>
<tr>
<td><input type="submit" value="Submit"/></td>
</tr>
</table>
</form:form>
</body>
在我的 POM 中:
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
编辑 1(问题): 是否有一种方法可以阻止 Spring 尝试将 MultipartFile 转换为 "Submit-time" 处的 Blob,从而更快地触发此操作,让 POST 方法已经管理了 Candidate(已经设置了 Blob 字段) ) 对象?
编辑 2: 正如 JB Nizet 所建议的,我尝试使用支持 POJO,它具有 MultipartFile 类型的字段 CV,通过表单(文本字段 + 文件字段)临时存储我 post 的内容,但我没有得到那个异常了,因为在 "Submit-time" 处填充的对象具有上传文件类型的 cv 字段:
import org.springframework.web.multipart.MultipartFile;
public class CandidatePOJO {
private Long id;
private String ssn;
private String name;
private String surname;
private String technology;
private String media;
private MultipartFile cv;
private boolean activeCV;
@Override
public String toString() {
return "CandidatePOJO [id=" + id + ", ssn=" + ssn + ", name=" + name + ", surname=" + surname + ", technology="
+ technology + ", media=" + media + ", cv=" + cv + ", activeCV=" + activeCV + "]";
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getSsn() {
return ssn;
}
public void setSsn(String ssn) {
this.ssn = ssn;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getSurname() {
return surname;
}
public void setSurname(String surname) {
this.surname = surname;
}
public String getTechnology() {
return technology;
}
public void setTechnology(String technology) {
this.technology = technology;
}
public String getMedia() {
return media;
}
public void setMedia(String media) {
this.media = media;
}
public MultipartFile getCv() {
return cv;
}
public void setCv(MultipartFile cv) {
this.cv = cv;
}
public boolean isActiveCV() {
return activeCV;
}
public void setActiveCV(boolean activeCV) {
this.activeCV = activeCV;
}
}
controller中,现在我首先关心的是pojo能否正确实例化,所以我的GET-POST对是:
@GetMapping("/updateCandidate/{id}")
public String showUpdateUserForm(@PathVariable("id") Long id, Model model) {
CandidatePOJO candidatePOJO = new CandidatePOJO();
candidatePOJO.setId(id);
model.addAttribute("candidateForm", candidatePOJO);
return "updateCandidateForm";
}
@PostMapping("/updateCandidate/updateCandidateResult")
public String updateCandidate(@ModelAttribute("candidateForm") CandidatePOJO candidatePOJO) {
System.out.println("CANDIDATE POJO");
System.out.println(candidatePOJO.toString()); // here I notice id = null
/* MultipartFile to Blob conversion */
// MultipartFile file = candidatePOJO.getCv();
// InputStream iStream = file.getInputStream();
// long size = file.getSize();
// Session session = emf.unwrap(Session.class);
// Blob cv = Hibernate.getLobCreator(session).createBlob(iStream, size);
/* instantiating the entity object to be freezed in db */
// Candidate candidate = new Candidate();
// set all data from candidatePOJO..
// candidate.setCv(cv);
// service.addOrUpdateCandidate(candidate);
return "updateCandidateResult";
}
我得到了一个未设置 ID 的 CandidatePOJO 对象。我无法将 id 从 GET 传递到 POST。有谁知道什么是错的吗?
编辑 3: 很多天后,我选择了支持 POJO 的解决方案,令人难以置信的是,我注意到 ID 从 GET 方法传递到 POST 方法(我没有改变任何东西,我刚刚执行了经典的 Maven 项目就像我 post 编辑我的问题时所做的那样清洁)。不幸的是,我现在面临另一个异常(当然,在恢复之前在 POST 方法中注释的代码之后):
javax.persistence.PersistenceException: Hibernate cannot unwrap EntityManagerFactory as 'org.hibernate.Session'
如何解决?
编辑 4: 上面的异常解决了替换:
Session session = emf.unwrap(Session.class);
与:
EntityManager em = emf.createEntityManager();
Session session = (Session) em.getDelegate();
BY USING SUPPORT POJO,我终于解决了。确实我已经通过代码解决了。 post 中的代码是正确的 (EDIT 2)。当我经常进行Maven项目清理时,并没有发现错误。我在最后几个小时执行了它,令人难以置信的是我的代码有效。我猜不出魔法 :D 我不知道到底发生了什么。通过代码,我用两种不同的方式解决了。第一:
@Autowired
private EntityManagerFactory emf;
// ........
@GetMapping("/updateCandidate/{id}")
public String showUpdateUserForm(@PathVariable("id") Long id, Model model) {
Candidate candidate = service.getCandidate(id).get();
CandidatePOJO candidatePOJO = new CandidatePOJO();
candidatePOJO.setId(id);
candidatePOJO.setName(candidate.getName());
candidatePOJO.setSurname(candidate.getSurname());
candidatePOJO.setSsn(candidate.getSsn());
candidatePOJO.setMedia(candidate.getMedia());
candidatePOJO.setTechnology(candidate.getTechnology());
candidatePOJO.setActiveCV(candidate.isActiveCV());
model.addAttribute("candidateForm", candidatePOJO);
return "updateCandidateForm";
}
@PostMapping("/updateCandidate/updateCandidateResult")
public String updateCandidate(@ModelAttribute("candidateForm") CandidatePOJO candidatePOJO) throws IOException {
MultipartFile file = candidatePOJO.getCv();
InputStream iStream = file.getInputStream();
long size = file.getSize();
EntityManager em = emf.createEntityManager();
Session session = (Session) em.getDelegate();
Blob cv = Hibernate.getLobCreator(session).createBlob(iStream, size);
Candidate candidate = new Candidate();
candidate.setId(candidatePOJO.getId());
candidate.setName(candidatePOJO.getName());
candidate.setSurname(candidatePOJO.getSurname());
candidate.setSsn(candidatePOJO.getSsn());
candidate.setMedia(candidatePOJO.getMedia());
candidate.setTechnology(candidatePOJO.getTechnology());
candidate.setActiveCV(candidatePOJO.isActiveCV());
candidate.setCv(cv);
service.addOrUpdateCandidate(candidate);
return "updateCandidateResult";
}
第二个(同一个GET):
@PostMapping("/updateCandidate/updateCandidateResult")
public String updateCandidate(@ModelAttribute("candidateForm") CandidatePOJO candidatePOJO) throws IOException, SerialException, SQLException {
MultipartFile file = candidatePOJO.getCv();
Blob cv = new SerialBlob(file.getBytes());
Candidate candidate = new Candidate();
candidate.setId(candidatePOJO.getId());
candidate.setName(candidatePOJO.getName());
candidate.setSurname(candidatePOJO.getSurname());
candidate.setSsn(candidatePOJO.getSsn());
candidate.setMedia(candidatePOJO.getMedia());
candidate.setTechnology(candidatePOJO.getTechnology());
candidate.setActiveCV(candidatePOJO.isActiveCV());
candidate.setCv(cv);
service.addOrUpdateCandidate(candidate);
return "updateCandidateResult";
}
作为 Spring 的新手,我还不知道其中的区别。我将不胜感激解释。此外,我迟早要修改此代码,以摆脱对 POJO 的支持并仅使用实体对象:如果有人能解决我最初的问题,我将永远感激不已!