0

I am developing a simple app that use spring boot and jpa and thymeleaf, i need to upload the image to my database but when i click on submit on my page fields get inserted in database except image field. i read the different posts on the website but none of them was really close to my problem and i have no idea why it does not insert the file into the database. i have to say the image field located in recipe entity

controller

@Controller
@RequestMapping("/recipe")
public class RecipeController {
    RecipeRepository recipeRepository;
   IngredientRepository ingredientRepository;
    public RecipeController(RecipeRepository recipeRepository, IngredientRepository ingredientRepository) {
        this.recipeRepository = recipeRepository; 
        this.ingredientRepository = ingredientRepository; //// this is other repo which cause no problem
    }
    @GetMapping("/insert_recipe")
    public String insetRecipe(Model model){
        model.addAttribute("addRecipe",new Recipe());
        model.addAttribute("addingredient",new Ingredient()); // this is other entity which cause no problem
      return   "insert_recipe";
    }
    @PostMapping("/postrecipe")
    public String postRecipe(@ModelAttribute("addRecipe")@Valid Recipe recipe, BindingResult result, Model model, @ModelAttribute("addingredient") Ingredient ingredient)  {
        recipeRepository.save(recipe);
        long id=recipe.getId();
        Recipe u=recipeRepository.findById(id);
        //model.addAttribute("addingredient",recipe);
        ingredient.setRecipe(u);
        ingredientRepository.save(ingredient);
        return "redirect:/recipe/insert_recipe";
    }
}

view page

<!DOCTYPE html>
<html xmlns:th="https://www.thymeleaf.org">

<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<form th:action="@{/recipe/postrecipe}" th:object="${addRecipe}"  enctype="multipart/form-data" method="post"  >
des:    <input type="text" name="descriptiob"/>
    serving:    <input type="text" name="servings"/>
   for ingredient description <input type="text" name="description" th:object="${addingredient}">
    upload picture <input type="file" th:name="image">

    <input type="submit" value="submit">
</form>
<br/><br/>

</body>
</html>

repo

public interface RecipeRepository extends CrudRepository<Recipe,Long> {
    Recipe findById(long is);


}

entity

@Entity
public class Recipe {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String descriptiob;
    @Lob
    private Byte[] image;
    private Integer servings;
    //setter and getter method also are in this class

error

Field error in object 'addRecipe' on field 'image': rejected value [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@12c96ba6]; codes [typeMismatch.addRecipe.image,typeMismatch.image,typeMismatch.[Ljava.lang.Byte;,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [addRecipe.image,image]; arguments []; default message [image]]; default message [Failed to convert property value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'java.lang.Byte[]' for property 'image'; nested exception is java.lang.IllegalArgumentException: Cannot convert value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'java.lang.Byte' for property 'image[0]': PropertyEditor [org.springframework.beans.propertyeditors.CustomNumberEditor] returned inappropriate value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile']]

enter image description here

GitHub link

pooya
  • 11
  • 1
  • 4
  • I don't know much about thymeleaf, but every input field on your view page has an attribute "name" except the input with type "file" it has "th:name" is that set up correctly? – Manuel Waltschek Aug 18 '20 at 20:00
  • i change it to `name=` now i have a error.i will post it on now – pooya Aug 18 '20 at 20:15
  • just use **name** attribute of html **input** tag and use the same name in controller using **@RequestParam("user_custom_name") MultipartFile user_custom_name** – Bal Vikash Sharma Sep 05 '21 at 18:44

2 Answers2

2

Let's have a look at the thymeleaf fragment

upload picture <input type="file" th:name="image">

and the error message we get:

Field error in object 'addRecipe' on field 'image': (...)
Cannot convert value of type '(...) StandardMultipartHttpServletRequest$StandardMultipartFile'
(...)to required type 'java.lang.Byte' for property 'image[0]': PropertyEditor (...)
upload picture <input type="file" th:name="image">

The name image colides with the Recipe field which has a different type (Byte[] than the MultipartFile we are trying to pass in the request).

One way to do it may be:

Step I. Change the th:name="image" to something else (that does not collide with the field names), e.g. th:name="imagefile"

upload picture <input type="file" th:name="imagefile">

Step II. Change the @RequestParam name to imagefile and convert the MultipartFile to the Byte[] before saving it.

    @PostMapping("/postrecipe")
    public String postRecipe(@ModelAttribute("addRecipe") Recipe recipe,
                             Model model,
                             @ModelAttribute("addingredient")@Valid Ingredient ingredient,
                             BindingResult bindingResult,
                             @RequestParam("imagefile") MultipartFile file, // changed from 'image'
                             @RequestParam("unitid") long id) throws IOException {
      long myid=id;
        recipeRepository.save(recipe);
        long ids=recipe.getId();
        Recipe u=recipeRepository.findById(ids);
        model.addAttribute("addingredient",recipe);
       UnitOfMeasure ob=unitOfMeasureRepository.findById(myid);

       Byte[] byteObjects = convertToBytes(file); // we have to convert it to Byte[] array
       u.setImage(byteObjects);
        recipeRepository.save(u); // TODO refactor - save once

        ingredient.setRecipe(u);
        ingredient.setUnitOfMeasure(ob);
        ingredientRepository.save(ingredient);
        return "redirect:/recipe/insert_recipe";
    }

    private Byte[] convertToBytes(MultipartFile file) throws IOException {
        Byte[] byteObjects = new Byte[file.getBytes().length];
        int i = 0;
        for (byte b : file.getBytes()) {
            byteObjects[i++] = b;
        }
        return byteObjects;
    }

Additional remarks:

  • Have a look at how Sfg handles the image upload and the displaying of it in the tutorial repository
  • It would be better to move the MultiPartFile to Byte[] conversion to the separate service (see the Sfg's repo / tutorial)

Edit:

Answering the question from the comment: I do not use xampp. The .bin extension suggests it is a binary file (makes sense as the image file is stored as the byte array).

Below is the snippet which should let you display the image in the browser instead.

IOUtils is from (import org.apache.tomcat.util.http.fileupload.IOUtils;)

@GetMapping("{id}/recipeimage")
public void renderImageFromDb(@PathVariable Long id, HttpServletResponse response) throws IOException {
    Recipe recipe = recipeRepository.findById(id).get();
    byte[] byteArray = new byte[recipe.getImage().length];

    int i = 0;
    for (Byte wrappedByte: recipe.getImage()) {
        byteArray[i++] = wrappedByte; // auto unboxing
    }

    response.setContentType("image/jpeg");
    InputStream is = new ByteArrayInputStream(byteArray);
    IOUtils.copy(is, response.getOutputStream());
}

If you know the id of the recipe, just type localhost:8080/recipe/<recipe id>/recipeimage

kasptom
  • 2,363
  • 2
  • 16
  • 20
  • thanks for your attention. there is a point i must say, after i did as you post here it was error on the `u.setImage(byteObjects)` and it asks me to change the 1th parameter of `setimage()` from byte[] to the Byte[] and then i did the same modification on the image field and get method so after that i run the program and i am able to upload the image to the database but still there is some thing seems not well! . i am using xampp as my database so when i go to the table page in xampp and i want to download the file that i uploaded i receive the file with format `.bin` do you have any idea why? – pooya Aug 19 '20 at 07:13
0

Concerning your problem that the input is not bound to the ModelAttribute:

change th:name to name on your input field.

Concerning your type error:

Maybe this could help you: Upload files to the @ModelAttribute using Thymeleaf

You need to use the correct type which is MultipartFile for your image. Consider using another class called e.g. RecipeDto on your Controllers method signature. Map this to your Recipe Entity so you can somehow convert the MultipartFile to a Byte array manually.

edit: org.springframework.web.multipart.MultipartFile#getBytes might do this for you


Concerning DTO: What are the DAO, DTO and Service layers in Spring Framework?

https://www.baeldung.com/entity-to-and-from-dto-for-a-java-spring-application

Manuel Waltschek
  • 515
  • 1
  • 6
  • 23
  • thanks for your attention but i got confused about what should i code in my controller and dao for that. i know about service layer and dao but how must be coded for this task make me confused and hopeless. can you post the code for dao and controller? – pooya Aug 18 '20 at 20:45
  • I was talking about a DTO actually. So you have another class for Recipe (RecipeDto) which is used on your postrecipe signature. The RecipeDto has a field called image (as well as all other input fields as class fields). The image is of type MultipartFile. In your postrecipe method you map your RecipeDto to your Recipe entity, setting the bytes from the MultipartFile. I am on mobile right now and cannot produce code atm. – Manuel Waltschek Aug 18 '20 at 20:46
  • i will continue like what you said if it didn't work again i will comment again here – pooya Aug 18 '20 at 21:00
  • i add a image on the post and i tried to see what is happening with debager and i find out it does not get to the `postRecipe()` method i check out my method without picture field it works fine so the method works when i want to inset other field. i also add the github link here – pooya Aug 19 '20 at 05:15