How to upload files to the #ModelAttribute using Thymeleaf?
I'am doing something that:
upload.html
<form method="POST" action="#" th:action="#{/sending}" th:object="${collage}" enctype="multipart/form-data" >
<input type="file" th:field="*{picture}" />
<input type="file" th:field="*{picture}" />
<input type="submit" value="upload" />
</form>
My controller:
#Controller
public class MainController {
#GetMapping(value = { "/" })
public String index(){
return "upload";
}
#GetMapping("/collage")
public String paintPicture(Model model){
return "collage";
}
#PostMapping("/sending")
public String redirect(#ModelAttribute(value="collage") Collage collage, RedirectAttributes redirectAttr) {
Collections.shuffle(Arrays.asList(collage.getCollage()));
redirectAttr.addFlashAttribute("pictures",collage.getCollage());
return "redirect:/collage";
}
}
Collage.class:
public class Collage {
private MultipartFile[] pictures;
public Collage(){}
public MultipartFile[] getCollage() {
return pictures;
}
public void setCollage(MultipartFile[] pictures) {
this.pictures = pictures;
}
}
I'm getting: java.lang.IllegalStateException: Neither BindingResult nor plain target object for bean name 'collage' available as request attribute in the console and a text on "/" page:
One picture is better than 1000 words:
Now code example to upload an array of multipartfiles inside of Entity:
<form action="#" th:action="#{/distribution/save}" class="form-horizontal"
role="form" method="post" th:object="${news}" enctype="multipart/form-data">
<input type="hidden" name="id" value="id" th:field="*{id}"> <div class="form-group has-label"> <label for="inputTitle" th:text="#{news.title}">TÃtulo</label>
<input type="text" class="form-control" id="inputTitle" th:placeholder="#{news.title}" th:field="*{title}"></div>
<input type="file" name = "multipartFilesDocument" value="multipartFilesDocument" th:field="*{multipartFilesDocument}" multiple="multiple"/>
<button type="submit" class="btn btn-default"><span th:text="#{common.save}"></span></button>
</div>
</form>
Controller Code:
#PostMapping("/save")
public String saveMultiparthFile(Model model,#ModelAttribute NewsDTO eventDTO){
eventDTO.getId();
return getrDetail(model);
}
Entity code:
public class NewsDTO {
private List<MultipartFile> multipartFilesDocument;
public List<MultipartFile> getMultipartFilesDocument() {
return multipartFilesDocument;
}
public void setMultipartFilesDocument(List<MultipartFile> multipartFilesDocument) {
this.multipartFilesDocument = multipartFilesDocument;
}
}
In this code is really important enctype="multipart/form-data" and name = "multipartFilesDocument" value="multipartFilesDocument" in form
you can apply this changes
1) change #ModelAttibute to #RequestParam
2) use MultipartFile[] as param and only use a single input file html
//name of input html should be collage
#PostMapping("/sending")
public String redirect(#RequestParam("collage") MultipartFile[] files, RedirectAttributes redirectAttr) {
Collections.shuffle(Arrays.asList(files));
redirectAttr.addFlashAttribute("pictures",files);
return "redirect:/collage";
}
and your html page
<form method="POST" th:action="#{/sending}" enctype="multipart/form-data" >
<input type="file" name="collage" multiple="multiple"/>
<input type="submit" value="upload" />
</form>
Related
I have a problem with thymeleaf. It can not find newUser model in the html file.
<form th:action="#{/users/add}" th:object="${newUser}" method="post">
<p>User Name: <input type="text" th:field="*{userName}"></p>
<p>Email: <input type="text" th:field="*{email}"></p>
<p>Password: <input type="text" th:field="*{password}"></p>
<p>Role:
<select th:field="*{role}">
<option th:each="role: ${roles}" th:value="${role.getId()}" th:utext="${role.getUserRole()}">
</option>
</select>
</p>
<p><input type="submit" value="Add User"></p>
</form>
From this class:
package Application.Controllers;
#Controller
#RequestMapping("/")
public class TemplateController {
private final RoleService roleService;
#Autowired
public TemplateController(RoleService roleService) {
this.roleService = roleService;
}
#GetMapping("register")
public String registerUser(Model model){
model.addAttribute("roles", roleService.listRoles());
model.addAttribute("newUser", new Users());
return "redirect:login";
}
}
The problem is that from other class and html it works fine.
Could you tell me what is wrong?
In your LoginUser make sure to add this line:
model.addAttribute("roles", roleService.listRoles());
model.addAttribute("newUser", new Users());
Or change the registerUser return just the view don't redirect to another action:
#GetMapping("register")
public String registerUser(Model model){
model.addAttribute("roles", roleService.listRoles());
model.addAttribute("newUser", new Users());
return "login";
}
I have a SpringBoot app. with this thymelaf template, that works fine when submitting:
<div class="form-group required-control">
<label for="gre">GRE</label>
<input id="gre" type="checkbox" name="gre" th:checked="*{gre}" th:onclick="submit()" />
</div>
but when I add another checkbox, It always take in account the first one, regardless which one I click
<div class="form-group required-control">
<label for="gre">GRE</label>
<input id="gre" type="checkbox" name="gre" th:checked="*{gre}" th:onclick="submit()" />
<label for="gre2">GRE2</label>
<input id="gre2" type="checkbox" name="gre2" th:checked="*{gre2}" th:onclick="submit()" />
</div>
There is no technical problem here. I think there is a problem with your submit() function, because I created a normal form and tried your same instance, and all selection combinations worked correctly.
I am adding the entity, controller and html files respectively for example.
public class Example {
private boolean gre;
private boolean gre2;
public Example() {
}
// getter/setter ...
}
#Controller
#RequestMapping("/example")
public class ExampleController {
#GetMapping("/create")
public String createExample(Model model) {
model.addAttribute("example", new Example());
return "example-form";
}
#PostMapping("/insert")
public String insertExample(Model model, Example example) {
model.addAttribute("example", example);
return "example-form";
}
}
<!DOCTYPE HTML>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<body>
<div>
<form action="/example/insert" method="post" th:object="${example}">
<div class="form-group required-control">
<label for="gre">GRE</label>
<input id="gre" type="checkbox" name="gre" th:checked="*{gre}" />
<label for="gre2">GRE 2</label>
<input id="gre2" type="checkbox" name="gre2" th:checked="*{gre2}" />
</div>
<button type="submit">Submit Form</button>
</form>
</div>
</body>
</html>
If you don't want to add an attribute into the Model, you can receive checkbox condition via HttpServletRequest.
#GetMapping("/create")
public String createExample() {
return "example-form";
}
#PostMapping("/insert")
public String insertExample(User user, HttpServletRequest request) {
user.setGre(request.getParameter("gre") != null);
user.setGre2(request.getParameter("gre2") != null);
userServiceImp.updateUser(editedUser); //for example updating user in database
return "/";
}
HTML will be like this:
<form th:action="/insert" method="post">
<div class="form-group required-control">
<label>GRE</label>
<input type="checkbox" name="gre"/>
<label>GRE 2</label>
<input type="checkbox" name="gre2"/>
</div>
<button type="submit">Submit Form</button>
</form>
I have got two methods that redirect to the same .html file.
First method is method responsible for saving, the second method is for updating.
It has got the same view so I just wanted to move user to the same view.
I have <h1>..</h1> in .html I would like to have title "New Mapping" for adding new mapping and "Update mapping" when we want to update mapping.
These are methods that do all the stuff (redirecting to the .html file)
#RequestMapping(path = "/.../update", method = RequestMethod.GET)
public String updatePage(#RequestParam("...") String ..., Model model) {
String[] tokens = ....split("_");
Template template= class.method(...);
model.addAttribute("template", template);
return "save";
}
#RequestMapping(path = "/.../save")
public String newMappingPage(Model model) {
Template template = new Template();
template.setCostIndex("10");
model.addAttribute("template", template);
return "save";
}
#RequestMapping(path = "/.../save", method = RequestMethod.POST)
public String saveMapping(#ModelAttribute Template template) {
class.method(template);
return "redirect:/main-page";
}
save.hml file
<html>
<head>
<title>Engine</title>
<link rel="stylesheet" th:href="#{/css/file.css}"/>
</head>
<body>
<h1>New/Update mapping</h1>
<form action="save" method="post" th:object="${template}">
<fieldset>
<label for="...">....[min]</label>
<input type="number" id="..." th:field="*{...}" step="0.1" min="0"/>
<label for="...">...</label>
<input type="number" id="...." th:field="*{...}" required="true"/>
<label for="...">....</label>
<input type="text" id="..." th:field="*{...}" maxlength="1024" size="50"/>
<br/>
<br/>
<label for="response">Response</label>
<br/>
<textarea id="response" rows="20" cols="150" th:field="*{...}" required="true"/>
<br/>
<input id="submit" type="submit" class="primary" value="Submit"/>
</fieldset>
</form>
</body>
</html>
You do not need to create two separate methods for add and update as you are storing data into DB from JSP page. use only one /save API.
#PostMapping(path = "/.../{}/save")
public String newMappingPage(Model model) {
Template template = new Template();
template.setCostIndex("10");
model = new Model();
if (model.getId() > 0) {
model = findById(model.getId());
model.addAttribute("heading", "Update Mapping");
} else {
model.addAttribute("heading", "New Mapping");
}
//Call method to store data from jsp file
return "save";
}
In JSP :
<html>
<head>
<title>Engine</title>
<link rel="stylesheet" th:href="#{/css/file.css}"/>
</head>
<body>
<h1>${heading}</h1>
<form action="save" method="post" th:object="${template}">
<fieldset>
<label for="...">....[min]</label>
<input type="number" id="..." th:field="*{...}" step="0.1" min="0"/>
<label for="...">...</label>
<input type="number" id="...." th:field="*{...}" required="true"/>
<label for="...">....</label>
<input type="text" id="..." th:field="*{...}" maxlength="1024" size="50"/>
<br/>
<br/>
<label for="response">Response</label>
<br/>
<textarea id="response" rows="20" cols="150" th:field="*{...}" required="true"/>
<br/>
<input id="submit" type="submit" class="primary" value="Submit"/>
</fieldset>
</form>
</body>
</html>
Update your method like this:
#PostMapping(path = "/.../{}/save")
public ModelAndView save(Model model) {
ModelAndView obj = new ModelAndView("save");
model = new Model();
if (model.getId() > 0) {
model = findById(model.getId());
obj .addAttribute("heading", "Update Mapping");
} else {
obj .addAttribute("heading", "New Mapping");
}
//Call method to store data from jsp file
return obj;
}
I should implement like execute(#ModelAttribute RegisterForm registerForm, Model model) in Controller class below.
I forgot put the #ModelAttribute.
However it works. When is name in RegisterForm class stored in the value that's sent from html?
Part of Controller class
#Controller
public class RegisterController {
#RequestMapping(value = "/register/")
public String register() {
return "register";
}
#RequestMapping(value="/register/execute", method=RequestMethod.POST)
//(#ModelAttribute RegisterForm registerForm, Model model) is better
public String execute(RegisterForm registerForm, Model model) {
model.addAttribute("registered_name", registerForm.getName());
return "index";
}
}
Form class
public class RegisterForm {
private String name; //when is this property stored in the value??
public String getName() {
return name;
}
}
Part of HTML file
<div class="input">
<p>Enter your name</p>
<input type="text" name="name" />
</div>
Please let me know if you need more information to solve this.Thanks.
add setter name to your class RegisterForm it's important
you should lik this :
<form action="register/execute">
<div class="input">
<p>Enter your name</p>
<input type="text" name="name" />
</div>
<form>
or usign tag form spring :
add this in your controller
#RequestMapping(value="/index", method=RequestMethod.POST)
public String executet(Model model) {
model.addAttribute("registerForm ", new RegisterForm ());
return "index";
}
include this code in your jsp
<%# taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<form:form method="POST" modelAttribute="registerForm ">
<form:input path="name" />
<input type="submit" value="Register">
</form:form>
I need to load an unknown number of files at once.
I found an example, and it works for a known amount of files:
markup:
<form method="POST" enctype="multipart/form-data">
<input name="files[0]" type="file" />
<input name="files[1]" type="file" />
<input type="submit" value="Send"/>
</form>
code:
#RequestMapping(method = RequestMethod.POST)
public String savePhoto(#ModelAttribute("album") Album album, BindingResult result, SessionStatus status, MultiPartFileUploadBean file)
{
List<MultipartFile> images = file.getFiles();
for (MultipartFile photo : images) {
...
}
return "redirect:/albums/"+album.getId();
}
MultiPartFileUploadBean:
public class MultiPartFileUploadBean {
private List<MultipartFile> files;
public void setFiles(List<MultipartFile> files) {
this.files = files;}
public List<MultipartFile> getFiles() {
return files;}
}
Yes, it works, but I do not know how the user wants to upload a photo in the album.
And I use:
<input name="files[]" type="file" multiple="multiple" />
I'll get a error.
Request processing failed; nested exception is
java.lang.NumberFormatException: For input string: ""
I was looking for how to use multiple = "multiple", but found none. I hope for your help.
In your XHTML:
<input name="files" type="file" multiple="multiple" />
Alter the request-mapped method:
#RequestMapping(method = RequestMethod.POST)
public String savePhoto(MultipartRequest multipartRequest, ...)
{
List<MultipartFile> images = multipartRequest.getFiles("files");
...
}