Good day. I want to store an image in an h2 database, then retrieve and display the same image in an html page. I am using spring boot and file upload method, but I am getting errors in the binding results
Here are the pages/classes:
Category.java
package com.vishal.project.entities;
@Entity
@Table(name="category")
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name="ID")
private Long id;
@Size(min=1, max=90)
@Column(name="CATEGORY_NAME")
private String CategoryName;
@Lob
@Column(name="CATEGORY_PHOTO")
private byte[] CategoryPhoto;
public Category(Long id, @Size(min = 1, max = 90) String categoryName, byte[] categoryPhoto) {
super();
this.id = id;
CategoryName = categoryName;
CategoryPhoto = categoryPhoto;
}
public byte[] getCategoryPhoto() {
return CategoryPhoto;
}
public void setCategoryPhoto(byte[] categoryPhoto) {
CategoryPhoto = categoryPhoto;
}
public Category() {}
@OneToMany(mappedBy = "category", cascade=CascadeType.ALL, orphanRemoval=true)
private Set<Book> Books = new HashSet<>();
public Set<Book> getBooks() {
return Books;
}
public void setBooks(Set<Book> books) {
Books = books;
}
public Long getId() {
return id;
}
public void setCategoryID(Long id) {
this.id = id;
}
public String getCategoryName() {
return CategoryName;
}
public void setCategoryName(String categoryName) {
CategoryName = categoryName;
}
@Override
public String toString() {
return "Category ID:" + id +
"Category Name:"+ CategoryName;
}
}
Categorycontroller.java
package com.vishal.project.web;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import com.vishal.project.entities.Category;
import com.vishal.project.services.CategoryService;
import com.vishal.project.util.Message;
@Controller
@RequestMapping(value="/categories")
public class CategoryController {
private final Logger logger = LoggerFactory.getLogger(BookController.class);
@Autowired
private MessageSource messageSource;
@Autowired
private CategoryService categoryService;
@GetMapping
public String list(Model uiModel) {
logger.info("Listing categories:");
List<Category> categories = categoryService.findALL();
uiModel.addAttribute("categories", categories);
logger.info("No. of categories: " + categories.size());
return "categories";
}
@GetMapping(value = "/{id}" , consumes="Multipart/formdata")
public String show(@PathVariable Long id, Model model) {
Category category = categoryService.findbyID(id);
model.addAttribute("category", category);
return "showCategory";
}
@GetMapping(value = "/edit/{id}")
public String updateForm(@PathVariable Long id, Model model) {
model.addAttribute("category", categoryService.findbyID(id));
return "updateCategory";
}
@GetMapping(value = "/new")
public String create(Model uiModel) {
logger.info("creating Category ...");
Category category = new Category();
uiModel.addAttribute("category", category);
return "updateCategory";
}
@PostMapping(value = "/upload")
public String saveCategory(@Valid @ModelAttribute("category") Category category, BindingResult bindingResult,
Model uiModel, HttpServletRequest httpServletRequest, RedirectAttributes redirectAttributes,
Locale locale, @RequestParam(value="file", required=true) MultipartFile file) {
logger.info("Creating Category....");
logger.info("Category ID" + category.getId());
logger.info("Category ID" + category.getCategoryName());
logger.info("Category ID" + category.getCategoryPhoto());
if(bindingResult.hasErrors())
{
logger.info("Error:", bindingResult.getAllErrors());
logger.debug("field Error:", bindingResult.getFieldError());
uiModel.addAttribute("message", new Message("error", messageSource.getMessage("category_save_fail", new Object[] {}, locale)));
uiModel.addAttribute("category", category);
return "updateCategory";
}
uiModel.asMap().clear();
redirectAttributes.addFlashAttribute("message",
new Message("success", messageSource.getMessage("Category_save_success", new Object[] {}, locale)));
//process upload file
logger.info("File Name :", file.getName() );
logger.info("File Size :", file.getSize() );
logger.info("File content type :", file.getContentType() );
if(file != null) {
byte[] filecontent = null;
try
{
InputStream inputStream = file.getInputStream();
if(inputStream == null)
logger.debug("file InputStream is null");
filecontent = IOUtils.toByteArray(inputStream);
category.setCategoryPhoto(filecontent);
}catch(IOException ex) {
logger.error("Error Saving uploaded file");
}
category.setCategoryPhoto(filecontent);
}
categoryService.save(category);
return "redirect:/categories/" + category.getId().toString();
}
}
categoryShow.page
<body>
<div th:replace="fragments/header_admin :: header_admin">Header</div>
<div class="container">
<h1>Category Details</h1>
<div>
<form class="form-horizontal" th:object="${category}" >
<input type="hidden" th:field="*{id}"/>
<div class="form-group">
<label class="col-sm-2 control-label">Category Name:</label>
<div class="col-sm-10">
<p class="form-control-static" th:text="${CategoryName}">
</p></div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label" >Category Photo</label>
<div class="col-sm-10">
<p class="form-control-static" ><img alt="CatName"
th:src="@{CategoryPhoto}" /> </p></div>
</div>
</form>
</div>
categoryUpdate page (create or update a Category with details and image)
<div class="container">
<h1>Category Details</h1>
<div>
<form class="form-horizontal" th:object="${category}" th:action="@{/categories/upload}" method="post" enctype="multipart/form-data">
<input type="hidden" th:field="*{id}"/>
<div class="form-group">
<label class="col-sm-2 control-label">Category Name</label>
<div class="col-sm-10">
<input class="form-control" th:field="*{CategoryName}"/>
</div>
</div>
<div class="form-group">
<label class="col-sm-2 control-label">Category Photo</label>
<div class="col-sm-10">
<input name="file" type="file" class="form-control" th:field="*{CategoryPhoto}"/>
</div>
</div>
<div class="row">
<button class="btn btn-default">Save</button>
</div>
</form>
</div>
<div th:insert="~{fragments/footer :: footer}">© 2017 Iuliana Cosmina & Apress</div>
The error : I am getting in bindingResult of CategoryController.saveCategory() method.
When I debug the code I am getting the error. Here's a pic to demonstrate:
I am having a hard time showing images on the CategoryShow page using thymleaf. Any help would be appreciated.
Update: Can anyone tell me what this error means, please:
Failed to convert property value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'byte[]' for property 'CategoryPhoto'; nested exception is
java.lang.IllegalArgumentException: Cannot convert value of type 'org.springframework.web.multipart.support.
StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'byte' for property
'CategoryPhoto[0]': PropertyEditor [org.springframework.beans.propertyeditors.CustomNumberEditor] returned
inappropriate value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile'
**Final Update: I am getting this error : ** Required request part 'file' is not present
The Issues are resolved Just tell you what I did in my Case:
1) If you are uploading one file in the page then use Part file as an argument to your post Mapping in the controller method
2) To my Second issue the image was not rendering in html page using thymeleaf because I am using byte[] to persist in my database. So it was not resolved by thymeleaf. So I used Apache commons binary codec Base64 (can be added as an Gradle or Maven dependency) to convert my byte[] image to Base64 String so that it can resolved by thymleaf. like this:
then in HTML
hope this helps someone !! thanks.
Spring translates uploaded files as MultipartFile objects, so you cannot map it directly to an array of bytes.
You can get a bytes array from a MultipartFile using MultipartFile#getBytes().
In your case, you could use an intermediate object (like a
CategoryForm
) where the fieldCategoryPhoto
would be of typeMultipartFile
. Then, in your controller, you map it to theCategory
object you already have using the method I showed above.Hi your Thymeleaf form is resolved as follows the name attribute is removed if the th:field attribute is present
Which clearly states the error you are getting even though you mention as name="file" in file input it is resolved to name=categoryPhoto because of th:field="*{CategoryPhoto}" mentioned after the name tag.
use th:value="${product.name}" th:name="name" th:id="name" instead of th:field it will more flexible
What you always can do is to compare a working file upload example with yours.
Another thing that will be helpful to compare your input names with the name your controller's method expect your file.
If your posted code is still relevant, you can find the name "Fileimport" on your file input in the template, but in your controller you expect file ( @RequestParam(value="file", required=false) ).
Other things that help you in debugging:
If this did not help on you, then please update the post: update your code (template + controller, if changed) and give use a better stacktrace: on better I mean you should show not just the last N line of the stacktrace, but at least to the first line where the execution goes through your code (in other words the class name starts with your package), even better if the first Caused by or the second if it is meaningful) is there.