无法在 Spring 中将 MultipartFile 转换为 Blob
Posted
技术标签:
【中文标题】无法在 Spring 中将 MultipartFile 转换为 Blob【英文标题】:Cannot convert MultipartFile into Blob in Spring 【发布时间】:2020-04-11 03:17:58 【问题描述】:我正在尝试将上传的文件另存为 mysql 记录中的 Blob。我是春天的新手。当我上传文件后要保存记录时,当我的POST方法updateCandidate()
执行时,我得到了这个异常:
字段“cv”上的对象“candidateForm”中的字段错误:拒绝值 [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@59c09df6];代码 [typeMismatch.candidateForm.cv,typeMismatch.cv,typeMismatch.java.sql.Blob,typeMismatch];参数 [org.springframework.context.support.DefaultMessageSourceResolvable:代码 [candidateForm.cv,cv];论据 [];默认消息 [cv]];默认消息 [无法将类型“org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile”的属性值转换为属性“cv”所需的类型“java.sql.Blob”;嵌套异常是 java.lang.IllegalStateException:无法将类型“org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile”的值转换为属性“cv”所需的类型“java.sql.Blob”:没有匹配的编辑器或转换找到策略]
出了什么问题?如何解决?
我的实体:
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 转换为 Blob,从而更快地触发此操作,让 POST 方法已经管理候选对象(已设置 Blob 字段)对象?
编辑 2: 正如 JB Nizet 所建议的那样,我尝试使用具有 MultipartFile 类型字段 CV 的支持 POJO 来临时存储我通过表单(文本字段 + 文件字段)发布的内容,并且我不再得到那个异常,因为在“提交时间”填充的对象具有上传文件类型的 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;
在控制器中,现在我首先关心的是看是否可以正确实例化 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 方法中注释的代码之后):
javax.persistence.PersistenceException: Hibernate 无法将 EntityManagerFactory 解包为 'org.hibernate.Session'
如何解决?
编辑 4: 上面的异常解决了替换:
Session session = emf.unwrap(Session.class);
与:
EntityManager em = emf.createEntityManager();
Session session = (Session) em.getDelegate();
【问题讨论】:
这个异常说明了一切:你的方法有一个 Candidate 作为参数,因此 Spring 应该从请求的参数中填充它的属性。但它无法从您发送的多部分文件中填充 Blob 类型的属性。只是不要将您的持久实体用于您的 CandidateForm。使用专用类,它代表客户端发送的内容,而不是数据库行包含的内容。 我无法使用 Blob 作为该字段类型。因此,如果我做得好,它会在 POST 开始执行时尝试转换,并在无法执行时停止,因此它甚至无法详细说明我的编码转换(?)。你能写一些代码吗? :) 创建一个类。一个简单的 POJO,对于应该由表单发送的每个参数以及适当的类型都有 getter 和 setter。使用该类而不是候选者作为您的命令。将此命令对象中的数据复制到您的实体,应用必要的转换。 你好@JBNizet,正如你所说,我使用了 POJO。如果我编码正确,系统会抛出同样的异常,这证实了我认为不是对象类型(实体 bean、POJO)会造成混淆。如果确实我编码错误,我想弄清楚什么是正确的做法。我想向您展示我是如何编辑我的 GET 和 POST 的。我不知道如何打开这里的聊天框。请原谅我在英语方面可能出现的错误。谢谢 好吧,我遗漏了一些东西。现在它部分工作。我可以让系统实例化候选 POJO(cv 类型不是 Blob 而是 MultipartFile),但它没有设置 id,所以我的 POST 检索了一个未设置字段的对象 【参考方案1】:通过使用支持 POJO,我终于解决了。事实上,我之前已经通过代码解决了。帖子中的代码是正确的(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 的支持并仅使用实体对象:如果有人能解决我最初的问题,我将永远感激不尽!
【讨论】:
以上是关于无法在 Spring 中将 MultipartFile 转换为 Blob的主要内容,如果未能解决你的问题,请参考以下文章
无法在 Spring Boot 中将 ProblemHandler 设置为 ObjectMapper
无法使用 Hibernate 在 Spring Boot 中将 MongoDB 设置为自动递增
Spring Boot 应用程序无法在 Docker 中将事件发布到 Kafka
无法在 Spring Data Cloud Spanner 中将 java.sql.Timestamp 转换为 com.google.cloud.Timestamp
Spring rest 控制器“MultipartFile[] multipartFile”总是接收 [] 或 null 文件