Search code examples
mysqlspring-boothibernatemultipartform-datamultipartfile

File not uploading getting "Validation failed for argument [0] in public java.lang.String"


I'm trying to use a form to save text and a file name along with uploading a file to a specific folder. I have used entities here and will share all the code.

package com.rx.healthtechhub.entities;


import java.util.Date;

import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.format.annotation.DateTimeFormat.ISO;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import jakarta.persistence.Table;

@Entity
@Table(name = "medrx")
public class MedRx {

    @Id
    @Column(name = "id_medrx")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int idMedrx;
    private String nameMed;
    @Column(columnDefinition = "TEXT")
    private String icon;
    @Column(columnDefinition = "TEXT")
    private String pic;
    
    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "id_medrxgroup")
    private MedRxGroups medRxGroups;
    
    @ManyToOne(cascade = CascadeType.ALL)
    @JoinColumn(name = "id_manufacturer")
    private Manufacturer manufacturer;
    private String packSize;
    private Double unitPrice;
    
    @DateTimeFormat(iso = ISO.DATE_TIME)
    private Date lastUpdate;
    
    @DateTimeFormat(iso = ISO.DATE_TIME)
    private Date createDate;
    
    public MedRx() {
    }

    public int getIdMedrx() {
        return idMedrx;
    }

    public void setIdMedrx(int idMedrx) {
        this.idMedrx = idMedrx;
    }

    public String getNameMed() {
        return nameMed;
    }

    public void setNameMed(String nameMed) {
        this.nameMed = nameMed;
    }

    public String getIcon() {
        return icon;
    }

    public void setIcon(String icon) {
        this.icon = icon;
    }

    public String getPic() {
        return pic;
    }

    public void setPic(String pic) {
        this.pic = pic;
    }

    public MedRxGroups getMedRxGroups() {
        return medRxGroups;
    }

    public void setMedRxGroups(MedRxGroups medRxGroups) {
        this.medRxGroups = medRxGroups;
    }

    public Manufacturer getManufacturer() {
        return manufacturer;
    }

    public void setManufacturer(Manufacturer manufacturer) {
        this.manufacturer = manufacturer;
    }

    public String getPackSize() {
        return packSize;
    }

    public void setPackSize(String packSize) {
        this.packSize = packSize;
    }

    public Double getUnitPrice() {
        return unitPrice;
    }

    public void setUnitPrice(Double unitPrice) {
        this.unitPrice = unitPrice;
    }

    public Date getLastUpdate() {
        return lastUpdate;
    }

    public void setLastUpdate(Date lastUpdate) {
        this.lastUpdate = lastUpdate;
    }

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    @Override
    public String toString() {
        return "MedRx [idMedrx=" + idMedrx + ", nameMed=" + nameMed + ", icon=" + icon + ", pic=" + pic
                + ", medRxGroups=" + medRxGroups + ", manufacturer=" + manufacturer + ", packSize=" + packSize
                + ", unitPrice=" + unitPrice + ", lastUpdate=" + lastUpdate + ", createDate=" + createDate + "]";
    }

    
    
}

And here is my Controller -

@PostMapping("/add_medrx")
    public String saveMedRxPg(@ModelAttribute("medRx") MedRx medRx, 
                               @RequestParam("icon") MultipartFile icon, 
                               @RequestParam("pic") MultipartFile pic, 
                               Model model, RedirectAttributes redirectAttributes) {
        String msg;
        
        try {
            String newIcon = fileUploadService.save(icon);
            String newPic = fileUploadService.save(pic);

            medRx.setCreateDate(new Date());
            medRx.setLastUpdate(new Date());
            medRx.setIcon(newIcon);
            medRx.setPic(newPic);
            
            medRxService.addMedRx(medRx);

            msg = "Files uploaded successfully!";
            redirectAttributes.addFlashAttribute("message", msg);

        } catch (Exception e) {
            msg = "Could not upload the files. Error: " + e.getMessage();
            redirectAttributes.addFlashAttribute("message", msg);
        }

        return "redirect:/admin/medicines";
    }

@GetMapping("/add_medrx")
    public String addMedRxPg(Model model) {
        
        model.addAttribute("classActiveSettingsMedicines","active");
        return "admin/medicines";
        
    }


@GetMapping("/new_medicine")
    public String newMedRxPg(Model model) {
        List<MedRxGroups> medRxGroups = medRxGroupService.getMedRxGroups();
        List<Manufacturer> manufacturers = manufacturerService.getManufacturers();
        model.addAttribute("classActiveSettingsMedicines","active");
        model.addAttribute("medrx", new MedRx());
        model.addAttribute("medRxGroups", medRxGroups);
        model.addAttribute("manufacturers", manufacturers);
        
        return "admin/newmedex";
        
    }

And here is my Form code --

<form class="form-horizontal" th:action="@{/add_medrx}" enctype="multipart/form-data" method="post" th:object="${medrx}">
                            <div class="panel panel-default">
                                <div class="panel-heading">
                                    <h3 class="panel-title"><strong>New Medicine</strong></h3>
                                    <ul class="panel-controls">
                                        <li><a href="#" class="panel-remove"><span class="fa fa-times"></span></a></li>
                                    </ul>
                                </div>
                                <div class="panel-body">
                                    
                                </div>
                                <div class="panel-body">                                                                        
                                    
                                    <div class="form-group">
                                        <label class="col-md-3 col-xs-12 control-label">Name</label>
                                        <div class="col-md-6 col-xs-12">                                            
                                            <div class="input-group">
                                                <span class="input-group-addon"><span class="fa fa-pencil"></span></span>
                                                <input type="text" class="form-control" th:field="*{nameMed}"/>
                                            </div>                                            
                                            <span class="help-block">This is sample of text field</span>
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <label class="col-md-3 col-xs-12 control-label">Icon</label>
                                        <div class="col-md-6 col-xs-12">
                                            <!-- <input type="file" class="fileinput btn-primary" th:text="${icon}" id="icon" th:name="icon" title="Browse file" /> -->
                                            <input type="file" id="icon" name="icon" class="fileinput btn-primary" title="Browse file" />
                                            <span class="help-block">Upload your icon (.png) file</span>
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <label class="col-md-3 col-xs-12 control-label">Picture</label>
                                        <div class="col-md-6 col-xs-12">                                                                                                                                        
                                            <!-- <input type="file" class="fileinput btn-primary" th:text="${pic}" id="pic" th:name="pic" title="Browse file" /> -->
                                            <input type="file" id="pic" name="pic" class="fileinput btn-primary" title="Browse file" />
                                            <span class="help-block">Upload your image (.jpg) file</span>
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <label class="col-md-3 col-xs-12 control-label">Group</label>
                                        <div class="col-md-6 col-xs-12">                                                                                            
                                            <select class="form-control select" th:field="*{medRxGroups}">
                                                <option value=0>Select</option>
                                                <option th:each="medRxGroup : ${medRxGroups}" th:value="${medRxGroup.idMedrxgroup}" th:text="${medRxGroup.nameGeneric}">Option 2</option>
                                            </select>
                                            <span class="help-block">Select your Group <strong> Or </strong> <a th:href="@{/new_group}" class="btn btn-success" type="button">Add Group</a></span>
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <label class="col-md-3 col-xs-12 control-label">Manufacturer</label>
                                        <div class="col-md-6 col-xs-12">                                                                                            
                                            <select class="form-control select" th:field="*{manufacturer}">
                                                <option value=0>Select</option>
                                                <option th:each="manufacturer : ${manufacturers}" th:value="${manufacturer.idManufacturer}" th:text="${manufacturer.companyName}">Option 2</option>
                                            </select>
                                            <span class="help-block">Select your manufacturer <strong> Or </strong> <a th:href="@{/new_manufacturer}" class="btn btn-success" type="button">Add Manufacturer</a></span>
                                        </div>
                                    </div>
                                    
                                    <div class="form-group">
                                        <label class="col-md-3 col-xs-12 control-label">Pack Size</label>
                                        <div class="col-md-6 col-xs-12">                                            
                                            <div class="input-group">
                                                <span class="input-group-addon"><span class="fa fa-pencil"></span></span>
                                                <input type="text" class="form-control" th:field="*{packSize}"/>
                                            </div>                                            
                                            <span class="help-block">Write packet size</span>
                                        </div>
                                    </div>
                                    <div class="form-group">
                                        <label class="col-md-3 col-xs-12 control-label">Unit Price</label>
                                        <div class="col-md-6 col-xs-12">                                            
                                            <div class="input-group">
                                                <span class="input-group-addon"><span class="fa fa-pencil"></span></span>
                                                <input type="text" class="form-control" th:field="*{unitPrice}"/>
                                            </div>                                            
                                            <span class="help-block">Write Unit Price</span>
                                        </div>
                                    </div>
                                    
                                <div class="panel-footer">
                                    <button class="btn btn-default">Clear Form</button>                                    
                                    <button class="btn btn-primary pull-right">Submit</button>
                                </div>
                            </div>
                            </form>

To manage file uploads, I have used a file upload service, which might help you understand the process better.

FileUploadService --

package com.rx.healthtechhub.services;

import java.nio.file.Path;
import java.util.stream.Stream;

import org.springframework.core.io.Resource;
import org.springframework.web.multipart.MultipartFile;

public interface FileUploadService {

    public void init();

      public String save(MultipartFile file);

      public Resource load(String filename);
      
      public void deleteAll();

      public Stream<Path> loadAll();
    
}

FileStorageServiceImpl --

package com.rx.healthtechhub.services;

import java.io.IOException;
import java.net.MalformedURLException;
import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;
import java.util.stream.Stream;

import org.springframework.core.io.Resource;
import org.springframework.core.io.UrlResource;
import org.springframework.stereotype.Service;
import org.springframework.util.FileSystemUtils;
import org.springframework.web.multipart.MultipartFile;

@Service
public class FilesStorageServiceImpl implements FileUploadService {

    private final Path root = Paths.get("./src/main/resources/static/img/uploads/");
    
    @Override
      public void init() {
        try {
          Files.createDirectories(root);
        } catch (IOException e) {
          throw new RuntimeException("Could not initialize folder for upload!");
        }
      }

    @Override
      public String save(MultipartFile file) {
        try {
            
            String newFileName = generateNewFileName(file.getOriginalFilename());
            
          Files.copy(file.getInputStream(), this.root.resolve(newFileName));
          
          return newFileName;
          
        } catch (Exception e) {
          if (e instanceof FileAlreadyExistsException) {
            throw new RuntimeException("A file of that name already exists.");
          }

          throw new RuntimeException(e.getMessage());
        }
      }

    private String generateNewFileName(String originalFilename) {
        String extension = originalFilename.substring(originalFilename.lastIndexOf('.'));
        String dateTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        String randomId = UUID.randomUUID().toString().replace("-", "");
        return "manufacturer_" + dateTime + "_" + randomId + extension;
    }
    
    @Override
      public Resource load(String filename) {
        try {
          Path file = root.resolve(filename);
          Resource resource = new UrlResource(file.toUri());

          if (resource.exists() || resource.isReadable()) {
            return resource;
          } else {
            throw new RuntimeException("Could not read the file!");
          }
        } catch (MalformedURLException e) {
          throw new RuntimeException("Error: " + e.getMessage());
        }
      }

    @Override
      public void deleteAll() {
        FileSystemUtils.deleteRecursively(root.toFile());
      }

    @Override
      public Stream<Path> loadAll() {
        try {
          return Files.walk(this.root, 1).filter(path -> !path.equals(this.root)).map(this.root::relativize);
        } catch (IOException e) {
          throw new RuntimeException("Could not load the files!");
        }
      }

}

I can't understand where I missed something, which is why I'm getting the following error.


[2m2024-06-18T12:17:29.770+06:00[0;39m [33m WARN[0;39m [35m6836[0;39m [2m---[0;39m [2m[rx.healthtechhub.co] [nio-8080-exec-9][0;39m [2m[0;39m[36m.w.s.m.s.DefaultHandlerExceptionResolver[0;39m [2m:[0;39m Resolved [org.springframework.web.bind.MethodArgumentNotValidException: Validation failed for argument [0] in public java.lang.String com.rx.healthtechhub.controller.HomeController.saveMedRxPg(com.rx.healthtechhub.entities.MedRx,org.springframework.web.multipart.MultipartFile,org.springframework.web.multipart.MultipartFile,org.springframework.ui.Model,org.springframework.web.servlet.mvc.support.RedirectAttributes) with 2 errors: [Field error in object 'medRx' on field 'icon': rejected value [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@3c008fd6]; codes [typeMismatch.medRx.icon,typeMismatch.icon,typeMismatch.java.lang.String,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [medRx.icon,icon]; arguments []; default message [icon]]; default message [Failed to convert property value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'java.lang.String' for property 'icon'; Cannot convert value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'java.lang.String' for property 'icon': no matching editors or conversion strategy found]] [Field error in object 'medRx' on field 'pic': rejected value [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@5321c0cb]; codes [typeMismatch.medRx.pic,typeMismatch.pic,typeMismatch.java.lang.String,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [medRx.pic,pic]; arguments []; default message [pic]]; default message [Failed to convert property value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'java.lang.String' for property 'pic'; Cannot convert value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'java.lang.String' for property 'pic': no matching editors or conversion strategy found]] ]

I'm trying to upload information through my application using a form. The form includes text fields, a file upload, and some options. However, I'm encountering an error when I fill out and submit the form. I suspect it might be a multipart file validation error, but I'm unsure how to resolve it.


Solution

  • I have resolved my issue. I missed a few things earlier. In my form code, I have kept everything the same. However, in my controller code, I have changed a few things. I’ve shared my code below.

    @PostMapping("/add_medrx")
        public String saveMedRx(@Valid @ModelAttribute("medRx") MedRx medRx, 
                                final @RequestParam(value = "file") MultipartFile file, 
                                final @RequestParam(value = "file2") MultipartFile file2, 
                                Model model) throws IOException {
                
            String msg = "";
                
            
                try {
                    String iconString = fileUploadService.save(file);
                    String picString = fileUploadService.save(file2);
                    medRx.setCreateDate(new Date());
                    medRx.setLastUpdate(new Date());
                    medRx.setIcon(iconString);
                    medRx.setPic(picString);
                    
                    medRxService.addMedRx(medRx);
                    
                    msg = "File Upload Successfully " + file + " & " + file2;
                    
                    System.out.println(msg);
                    
                } catch (Exception e) {
    
                    msg = "Could not upload the file: " + file + " & " + file2 + ". Error: " + e.getMessage();
                     
                    System.out.println(msg);
                    
                    e.printStackTrace();
                }
                
                List<MedRx> medRxs = medRxService.getMedRxs();
                
                model.addAttribute("medRxs", medRxs);
                model.addAttribute("classActiveSettingsMedicines","active");
                return "admin/medicines";
                
            }
    

    In my form code, I just changed the input names. The updated input code is shared below:

    <input type="file" name="file" class="fileinput btn-primary" title="Browse file" />
    <input type="file" name="file2" class="fileinput btn-primary" title="Browse file" />
    

    Thats it. Now it's working. Thank you so much.