How to upload and save image in MySQL via SpringBoot JPA - spring-boot

I'm trying to upload and save an image in MySQL with SpringBoot using JPA but unfortunately I'm greeted with this error, can anyone tell me what am I missing ?
Error
Field error in object 'institution' on field 'logo': rejected value [org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile#387c2996]; codes [typeMismatch.institution.logo,typeMismatch.logo,typeMismatch.[Ljava.lang.Byte;,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [institution.logo,logo]; arguments []; default message [logo]]; default message [Failed to convert property value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile' to required type 'java.lang.Byte[]' for property 'logo'; 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 'logo[0]': PropertyEditor [org.springframework.beans.propertyeditors.CustomNumberEditor] returned inappropriate value of type 'org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile']]
pom.xml
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
Entity
#Lob
private Byte[] logo;
controller
#PostMapping("/institutionAddNew")
public String addNewComp(#ModelAttribute("institution") Institution institution, Model model,
HttpSession session,
#RequestParam CommonsMultipartFile fileUpload) throws Exception {
Integer insId = (Integer) session.getAttribute("instId");
Byte[] byteObjects = new Byte[fileUpload.getBytes().length];
int i = 0;
for (byte b : fileUpload.getBytes()){
byteObjects[i++] = b;
}
institution.setLogo(byteObjects);
cs.addNewInstitution(institution);
model.addAttribute("institutionlist", cs.getAllInstitutions(insId));
return "redirect:/showAllInstitutions";
}
Form
<form:form action="/institutionAddNew" method="post" modelAttribute="institution" enctype="multipart/form-data">
<form:input path="instName" />
<input type="file" name="logo" />
<button type="submit" class="btn btn-primary">Submit</button>
</form:form>

The provided information is insufficient to properly diagnose the problem. You need to provide more information.
But I am guessing that, when the jsp file is sending data to your controller, it is sending the file as a MultiPartFile. But your application is trying to cast it to an byte array. That's why your are getting the exception. The problem does not lie withJPA, the exception should happen before the data enters the controller.
You can use an dto object to pass the data to your controller where the file is a Multipartfile object, then you can prepare the entity object with the dto object data. Here you can read the multipartfile and convert it to an byte array and then save it to your database.
Add another InstitutionParam class like
public class InstitutionParam {
private MultipartFile logo;
}
Keep your Institution class same. Your controller should look like
#PostMapping("/institutionAddNew")
public String addNewComp(#ModelAttribute("institution") InstitutionParam institution, Model model,
HttpSession session) throws Exception {
Integer insId = (Integer) session.getAttribute("instId");
MultipartFile fileUpload = institution.getLogo();
Byte[] byteObjects = new Byte[fileUpload.getBytes().length];
int i = 0;
for (byte b : fileUpload.getBytes()){
byteObjects[i++] = b;
}
Institution entity = new Institution();
entity.setLogo(byteObjects);
// save to db
cs.addNewInstitution(institution);
model.addAttribute("institutionlist", cs.getAllInstitutions(insId));
return "redirect:/showAllInstitutions";
}

I've managed to solve it eventually, I had to change the code. I'm sure there are many ways of doing this, not sure if this approached is the best one but it works for me.
Entity
private Integer instId;
private String fileName;
private String fileType;
#Lob
private byte[] image;
public Institution(Integer instId, String instName, String fileName, String fileType, byte[] image) {
super();
this.instId = instId;
this.instName = instName;
this.fileName = fileName;
this.fileType = fileType;
this.image = image;
}
Service
public Institution storeFile(Integer instId, String name, MultipartFile file) throws Exception {
String fileName = StringUtils.cleanPath(file.getOriginalFilename());
try {
Institution institution = new Institution(instId, name,fileName, file.getContentType(), file.getBytes());
return comrepos.save(institution);
} catch (IOException ex) {
throw new Exception("Could not store file " + fileName, e);
}
}
controller
#PostMapping("/institutionAddNew")
public String uploadImage(#RequestParam("image") MultipartFile file,
#RequestParam("instName") String name,
#RequestParam("instId") Integer instId) {
try {
iservice.storeFile(instId,name,file);
} catch (Exception e) {
e.printStackTrace();
}
return "redirect:/showAllInstitutions";
}
Form
<form:form action="/institutionAddNew" method="post" modelAttribute="institution" enctype="multipart/form-data">
<form:input path="instName" />
<input type="file" name="logo" />
<button type="submit" class="btn btn-primary">Submit</button>
</form:form>

Related

Trouble while trying to return page with error messages from entity fields

I have field like this:
#Min(value = 0, message = "Amount must be positive")
private double amount;
When I populate form and insert for example -1 for that field I get message on stack trace that I entered wrong value and this on HTML page: This application has no explicit mapping for /error, so you are seeing this as a fallback. So that works fine, but I want to create a error for user so he actually know what error he got, I want to display error on HTML page for him.
Followed by some tutorial in my Thymeleaf I have added this:
<div>
<label for="amount">Amount</label> <input type="text" th:field="*{amount}" id="amount">
<p th:if="${#fields.hasErrors('amount')}"
th:class="${#fields.hasErrors('amount')}? error">Invalid amount</p>
</div>
And in my controller I have this:
if (result.hasErrors()) {
return "new_transaction";
}
Now I will provide both controllers, one for showing form and one for processing form.
Controller to show form:
#GetMapping("/showNewTransactionForm/{id}")
public String showNewTransactionForm(#PathVariable(value = "id") long id, Transaction transaction, Model model) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
UserDetailsImpl user = (UserDetailsImpl) authentication.getPrincipal();
long userId = user.getId();
model.addAttribute("userId", userId);
model.addAttribute("transaction", transaction);
model.addAttribute("transactionType", TransactionType.values());
model.addAttribute("expenseCategories", ExpenseCategories.values());
model.addAttribute("incomeCategories", IncomeCategories.values());
return "new_transaction";
}
Controller to process form:
#PostMapping("/saveExpense/{walletId}")
public String saveExpense(#PathVariable(value = "walletId") long walletId,
#ModelAttribute("wallets") Transaction transaction, BindingResult result) {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
UserDetailsImpl user = (UserDetailsImpl) authentication.getPrincipal();
long userId = user.getId();
if (result.hasErrors()) {
return "new_transaction";
}
Wallet wallet = walletService.getWalletById(walletId);
transaction.setWallet(wallet);
transactionService.saveExpense(transaction, walletId, userId);
return "redirect:/api/wallet/userWallet/balance/" + userId;
}
And I obviously have a problem to return a right point here:
if (result.hasErrors()) {
return "new_transaction";
}
Instead of new_transaction I tried with return "redirect:/api/transaction/showNewTransactionForm/" + userId; but as before I get a error message in stacktrace but on html is Whitelabel Error Page

Unable to upload multiple images - Springboot

I'm trying to upload multiple images in Springboot.
Here is my form HTML:
<form th:action="#{${#httpServletRequest.requestURI}}" th:object="${formInput}" method="post" enctype="multipart/form-data" onsubmit="return validate();">
To take in multiple files:
<input type="file" onclick="radioButton()" onchange=" enableSubmit();" id = "fileInput" name="uploadingFiles[]" accept="image/*" multiple/>
Here is my controller:
#RequestMapping(value = "/userProfiles/{userId}/setup/Tool.html", method = RequestMethod.POST)
public void makeIAmVideo(#PathVariable Long userId, HttpServletResponse response, #RequestParam(value = "track_value") int trackNumber, #RequestParam("uploadingFiles") MultipartFile[] uploadingFiles) throws IOException {
UserProfile userProfile = userProfileRepository.findById(userId);
ArrayList<String> photos = new ArrayList<>();
String audioPath= audioPath1;
System.out.println(trackNumber);
for(MultipartFile file : uploadingFiles){
photos.add(file.getName());
}
}
But when I debug it, the photos are not uploaded in the photos array list, it's size stays 0.
Am I doing something wrong?
add these line in your form which contain this input field.
enctype="multipart/form-data"

SpringBoot: Large Streaming File Upload Using Apache Commons FileUpload

Am trying to upload a large file using the 'streaming' Apache Commons File Upload API.
The reason I am using the Apache Commons File Uploader and not the default Spring Multipart uploader is that it fails when we upload very large file sizes (~2GB). I working on a GIS application where such file uploads are pretty common.
The full code for my file upload controller is as follows:
#Controller
public class FileUploadController {
#RequestMapping(value="/upload", method=RequestMethod.POST)
public void upload(HttpServletRequest request) {
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (!isMultipart) {
// Inform user about invalid request
return;
}
//String filename = request.getParameter("name");
// Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload();
// Parse the request
try {
FileItemIterator iter = upload.getItemIterator(request);
while (iter.hasNext()) {
FileItemStream item = iter.next();
String name = item.getFieldName();
InputStream stream = item.openStream();
if (item.isFormField()) {
System.out.println("Form field " + name + " with value " + Streams.asString(stream) + " detected.");
} else {
System.out.println("File field " + name + " with file name " + item.getName() + " detected.");
// Process the input stream
OutputStream out = new FileOutputStream("incoming.gz");
IOUtils.copy(stream, out);
stream.close();
out.close();
}
}
}catch (FileUploadException e){
e.printStackTrace();
}catch (IOException e){
e.printStackTrace();
}
}
#RequestMapping(value = "/uploader", method = RequestMethod.GET)
public ModelAndView uploaderPage() {
ModelAndView model = new ModelAndView();
model.setViewName("uploader");
return model;
}
}
The trouble is that the getItemIterator(request) always returns an iterator that does not have any items (i.e. iter.hasNext() ) always returns false.
My application.properties file is as follows:
spring.datasource.driverClassName=org.postgresql.Driver
spring.datasource.url=jdbc:postgresql://localhost:19095/authdb
spring.datasource.username=georbis
spring.datasource.password=asdf123
logging.level.org.springframework.web=DEBUG
spring.jpa.hibernate.ddl-auto=update
multipart.maxFileSize: 128000MB
multipart.maxRequestSize: 128000MB
server.port=19091
The JSP view for the /uploader is as follows:
<html>
<body>
<form method="POST" enctype="multipart/form-data" action="/upload">
File to upload: <input type="file" name="file"><br />
Name: <input type="text" name="name"><br /> <br />
Press here to upload the file!<input type="submit" value="Upload">
<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
</body>
</html>
What might I be doing wrong?
Thanks to some very helpful comments by M.Deinum, I managed to solve the problem. I have cleaned up some of my original post and am posting this as a complete answer for future reference.
The first mistake I was making was not disabling the default MultipartResolver that Spring provides. This ended up in the resolver processing the HttpServeletRequest and thus consuming it before my controller could act on it.
The way to disable it, thanks to M. Deinum was as follows:
multipart.enabled=false
However, there was still another hidden pitfall waiting for me after this. As soon as I disabled default multipart resolver, I started getting the following error when trying to make an upload:
Fri Sep 25 20:23:47 IST 2015
There was an unexpected error (type=Method Not Allowed, status=405).
Request method 'POST' not supported
In my security configuration, I had enabled CSRF protection. That necessitated that I send my POST request in the following manner:
<html>
<body>
<form method="POST" enctype="multipart/form-data" action="/upload?${_csrf.parameterName}=${_csrf.token}">
<input type="file" name="file"><br>
<input type="submit" value="Upload">
</form>
</body>
</html>
I also modified my controller a bit:
#Controller
public class FileUploadController {
#RequestMapping(value="/upload", method=RequestMethod.POST)
public #ResponseBody Response<String> upload(HttpServletRequest request) {
try {
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (!isMultipart) {
// Inform user about invalid request
Response<String> responseObject = new Response<String>(false, "Not a multipart request.", "");
return responseObject;
}
// Create a new file upload handler
ServletFileUpload upload = new ServletFileUpload();
// Parse the request
FileItemIterator iter = upload.getItemIterator(request);
while (iter.hasNext()) {
FileItemStream item = iter.next();
String name = item.getFieldName();
InputStream stream = item.openStream();
if (!item.isFormField()) {
String filename = item.getName();
// Process the input stream
OutputStream out = new FileOutputStream(filename);
IOUtils.copy(stream, out);
stream.close();
out.close();
}
}
} catch (FileUploadException e) {
return new Response<String>(false, "File upload error", e.toString());
} catch (IOException e) {
return new Response<String>(false, "Internal server IO error", e.toString());
}
return new Response<String>(true, "Success", "");
}
#RequestMapping(value = "/uploader", method = RequestMethod.GET)
public ModelAndView uploaderPage() {
ModelAndView model = new ModelAndView();
model.setViewName("uploader");
return model;
}
}
where Response is just a simple generic response type I use:
public class Response<T> {
/** Boolean indicating if request succeeded **/
private boolean status;
/** Message indicating error if any **/
private String message;
/** Additional data that is part of this response **/
private T data;
public Response(boolean status, String message, T data) {
this.status = status;
this.message = message;
this.data = data;
}
// Setters and getters
...
}
If you're using a recent version of spring boot (I'm using 2.0.0.M7) then the property names have changed.
Spring started using technology specific names
spring.servlet.multipart.maxFileSize=-1
spring.servlet.multipart.maxRequestSize=-1
spring.servlet.multipart.enabled=false
If you're getting StreamClosed exceptions caused by multiple implementations being active, then the last option allows you to disable the default spring implementation
Please try to add spring.http.multipart.enabled=false in application.properties file.
I use kindeditor + springboot. When I use (MultipartHttpServletRequest) request. I could get the file, but I use appeche-common-io:upload.parse(request) the return value is null.
public BaseResult uploadImg(HttpServletRequest request,String type){
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
MultiValueMap<String, MultipartFile> multiFileMap = multipartRequest.getMultiFileMap();
You Can simply add spring properties:
spring.servlet.multipart.max-file-size=20000KB
spring.servlet.multipart.max-request-size=20000KB
here my maximum file size is 20000KB, you can change if required.

Spring portlet ajax call, No parameter found

I am trying to build a sample spring portlet, with LR 6.2 GA1.
Below is the source for the same https://docs.google.com/file/d/0By1kU5o_jlrublhUNXIxQ24wODQ/edit
On the ajax the parameters are not being fetched.The parameters always remain blank.
#Controller(value = "ProjectSearch")
#RequestMapping("VIEW")
public class ProjectSearch {
Log log_ = LogFactoryUtil.getLog(ProjectSearch.class);
#RenderMapping
public String handleRenderRequest(final RenderRequest request,
final RenderResponse response, Model model) {
System.out.println("ProjectSearch.handleRenderRequest()");
return "search_form";
}
#ResourceMapping("getProjectNameSuggestion")
public void getNameSuggestion(ResourceRequest request,
ResourceResponse response) throws IOException {
Map<String, String[]> map = request.getParameterMap();
for (Map.Entry<String, String[]> element : map.entrySet()) {
log_.info(element.getKey());
}
String entityName = ParamUtil.getString(request, "query");
log_.info("Entity name==>" + entityName);
}
}
#RenderMapping
public String handleRenderRequest(final RenderRequest request,
final RenderResponse response, Model model) {
System.out.println("ProjectSearch.handleRenderRequest()");
return "search_form";
}
#ResourceMapping("getProjectNameSuggestion")
public void getNameSuggestion(ResourceRequest request,
ResourceResponse response) throws IOException {
Map<String, String[]> map = request.getParameterMap();
for (Map.Entry<String, String[]> element : map.entrySet()) {
log_.info(element.getKey());
}
String entityName = ParamUtil.getString(request, "query");
log_.info("Entity name==>" + entityName);
}
}
Output-->05:23:24,148 INFO [http-bio-8080-exec-119][ProjectSearch:41] Entity name==>
Could any body tell me what is that I am doing wrong??
Solution:
Configure Requires Name Spaced Parameters to false in liferay-portlet.xml
Now need to do require Name spaced parameters to false then only form data is mapped in Action Request and Render Request. And also form data will be binding to model object or command object.
The following is configuration we need to do in liferay-portlet.xml file
<requires-namespaced-parameters>false</requires-namespaced-parameters>
Required Name Space Parameter Behavior in Liferay
Liferay 6.2 we have to append portlet Name space for every name of input element i.e. form input elements or request parameters names otherwise portlet action class ignore the parameters which does not have portlet name space to names.
Scenario
Jsp page
In the following form we are not appending portlet name space to form input element names.
<portlet:actionURL var="addEmployeeActionURL" name="addEmployee">
<portlet:param name="<%=ActionRequest.ACTION_NAME%>" value="addEmployee"/>
</portlet:actionURL>
<form action="<%=addEmployeeActionURL%>" name="emplyeeForm" method="POST">
Employee Name<br/>
<input type="text" name="employeeName" id="employeeName"/><br/>
Employee Address<br/>
<input type="text" name="employeeAddress" id="employeeName"/><br/>
<input type="submit" name="addEmployee" id="addEmployee" value="Add Employee"/>
</form>
Portlet Class Action Method
public class EmplyeePortletAction extends MVCPortlet {
public void addEmployee(ActionRequest actionRequest,
ActionResponse actionResponse) throws IOException, PortletException {
String employeeName=ParamUtil.getString(actionRequest,"employeeName");
String employeeAddress=ParamUtil.getString(actionRequest,"employeeAddress");
}
}
In above case employeeName and employeeAddress form input data not accessible in portlet class action .The form elements name are not appended with portlet name space such scenarios portlet class ignore those request parameters or form inputs
Solution:1
Need to append tag to every input element name.
Jsp page
<portlet:actionURL var="addEmployeeActionURL" name="addEmployee">
<portlet:param name="<%=ActionRequest.ACTION_NAME%>" value="addEmployee"/>
<portlet:param name="requestParam" value=" requestParamValue"/>
</portlet:actionURL>
<form action="<%=addEmployeeActionURL%>" name="emplyeeForm" method="POST">
Employee Name<br/>
<input type="text" name="<portlet:namespace/>employeeName" id="<portlet:namespace/>employeeName"/><br/>
Employee Address<br/>
<input type="text" name="<portlet:namespace/>employeeAddress" id="<portlet:namespace/>employeeName"/><br/>
<input type="submit" name="addEmployee" id="addEmployee" value="Add Employee"/>
</form>
Portlet Class Action Method
public class EmplyeePortletAction extends MVCPortlet {
public void addEmployee(ActionRequest actionRequest,
ActionResponse actionResponse) throws IOException, PortletException {
String employeeName=ParamUtil.getString(actionRequest,"employeeName");
String employeeAddress=ParamUtil.getString(actionRequest,"employeeAddress");
String requestParamValue=ParamUtil.getString(actionRequest,"requestParam");
}
}
Solution:2
We can make it false to following tag value in liferay-portlet.xml file
<requires-namespaced-parameters>false</requires-namespaced-parameters>
Solution:3
We can use alloy tag library form tags. When we use AUI tags it will append portlet name space to each input element name.
Jsp page
<%# taglib uri="http://liferay.com/tld/aui" prefix="aui" %>
<aui:input type="text" name="employeeAddress" id="employeeName"/><br/>
<aui:input type="submit" name="addEmployee" id="addEmployee" value="Add Employee"/
<input type="text" name="<portlet:namespace/>employeeAddress" id="<portlet:namespace/>employeeName"/>
Is same As
<aui:input type="text" name="employeeAddress" id="employeeName"/>
http://www.liferaysavvy.com/2013/12/liferay-spring-portlet.html
http://www.liferaysavvy.com/2014/04/liferay-mvc-portlet-development.html

Spring HTTP Status 400 - The request sent by the client was syntactically incorrect

I have a form which a user is to use to upload a file to a local drive and also persist some data to the database. But this error message has come up which I would like to know how to deal with it.
HTTP Status 400 - The request sent by the client was syntactically incorrect.
The controller
#RequestMapping(value = "/main/user/setter/addpage", method =
RequestMethod.POST, params = "save")
public String saveProcess(#ModelAttribute("module") Module module,
#RequestParam("userId") Integer userId,
#RequestParam("name") String name,
#RequestParam("file") MultipartFile file,
BindingResult result, HttpSession session) {
if (result.hasErrors()) {
return "redirect:/main/user/setter/settingpage";
}
else
if(module != null){
try {
MultipartFile filea = module.getFileData();
InputStream inputStream = null;
OutputStream outputStream = null;
if (filea.getSize() > 0) {
inputStream = filea.getInputStream();
outputStream = new FileOutputStream("C:\\Test\\"
+ filea.getOriginalFilename());
System.out.println("Uploaded File Name");
System.out.println(filea.getOriginalFilename());
int readBytes = 0;
byte[] buffer = new byte[8192];
while ((readBytes = inputStream.read(buffer, 0, 8192)) !=
-1) {
outputStream.write(buffer, 0, readBytes);
}
outputStream.close();
inputStream.close();
session.setAttribute("success", "File Uploaded");
session.setAttribute("uploadFile", "C:\\Test\\"
+ filea.getOriginalFilename());
}
} catch (Exception e) {
e.printStackTrace();
}
}
// Delegate to service
moduleService.add(userId, module);
return "redirect:/main/user/setter/settingpage";
}
The form itself
<c:url var="processUrl" value="/main/user/setter/addpage?userId=2" />
<form:form modelAttribute="module" method="POST" action="${processUrl}" name="module"
enctype="multipart/form-data">
<form:label path="fileName">Document Title:</form:label>
<form:input path="fileName" name="name"/><br/>
<form:label path="documentPath">Coursework Sample:</form:label>
<form:input path="documentPath" type="file" name="file" id="file" size="43.9"/><br/>
<form:label path="liveDate">Live Date:</form:label>
<form:input path="liveDate"/><br/>
<input type="submit" name="save" value="Save" id="save"/>
<input type="submit" name="send" value="Send" id="send"/>
</form:form>
I'm trying to save the form first the I will use a separate method to send it.
Your method is declared as such:
public String saveProcess(#ModelAttribute("module") Module module,
#RequestParam("userId") Integer userId,
#RequestParam("name") String name,
#RequestParam("file") MultipartFile file,
BindingResult result, HttpSession session) ...
Assuming that you are trying to apply the BindingResult to the Module instance, you need to change the method definition so that the BindingResult comes right after the Module instance.
public String saveProcess(#ModelAttribute("module") Module module,
BindingResult result,
#RequestParam("userId") Integer userId,
#RequestParam("name") String name,
#RequestParam("file") MultipartFile file,
HttpSession session) ...
See the reference for more details
The Errors or BindingResult parameters have to follow the model object
that is being bound immediately as the method signature might have
more that one model object and Spring will create a separate
BindingResult instance for each of them [...]
See here.
The error HTTP Status 400 - The request sent by the client was syntactically incorrect. can come from a missing multipartResolver in the context (servlet-context.xml)
`<beans:bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver" />`
For more info, see the replies from SpringMVC-FileUpload - The request sent by the client was syntactically incorrect

Resources