How to display @Lob image from mysql database using spring mvc and jsp


How to display @Lob image from mysql database using spring mvc and jsp



There is link to github: https://github.com/Lukszn/ProjectProfile I'm using Spring 4.3.7.RELEASE, MySQL Connector Java: 5.1.39 and hibrnate: 5.2.9. Finaly
There i have User and his Account model. In account i have @Lob accPicture and some Strings(+ get/set). I'm trying a lot of answers from stackoverflow and documentation to show Account image, but without success. Last think what I do : created own ImageController. I successfully stored image in database but when I'm trying to display it in my jsp, it is showing "HTTP Status 400 -
The request sent by the client was syntactically incorrect."
Firstly I show you my User model:


@Entity
@Table(name = "users")
public class User implements Serializable{

/**
*
*/
private static final long serialVersionUID = 1L;

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;

@Column(unique = true)
@NotBlank
@Size(min=4,max=20)
private String login;

@NotBlank
private String password;

@Column(unique = true)
@Email
@NotBlank
private String email;

private String permission;

@OneToMany()
private List<Account> accounts;


public User(final String login, final String password, final String email) {
Preconditions.checkArgument(!Strings.isNullOrEmpty(login));
Preconditions.checkArgument(!Strings.isNullOrEmpty(password));
Preconditions.checkArgument(!Strings.isNullOrEmpty(email));
this.login = login;
this.password = password;
this.email = email;
}

public User() {
}
}
+ get/set



Account model:


@Entity
@Table(name = "accounts")
public class Account {


@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;

private boolean ifBasicAccount;

private String accTitle;

private String accFirstName;

private String accLastName;

private String accBirthdate;

private String accPhoneNumber;

private String accEducation;

private String accExperience;

private String accAbilities;

private String accInterests;

private String accProjects;

private String accDescription;

@Lob
private byte accPicture;


@ManyToOne
private User user;


public Account() {
}

+ get/set



next Account Controller:


@Controller
public class AccountController {

@Autowired
AccountRepository accountRepository;

@Autowired
UserRepository userRepository;


@RequestMapping(method = RequestMethod.GET, value ="addAccount")
public String addAccount(Model model) {
Account account = new Account();
model.addAttribute("account", account);

return "addAccount";
}

@RequestMapping(method = RequestMethod.POST, value ="addAccount")
public String addAccount(@ModelAttribute Account account, HttpSession session) {
User user = userRepository.findOne((Long) session.getAttribute("user_id"));
account.setIfBasicAccount(false);
account.setUser(user);
accountRepository.save(account);
return "redirect:/accounts";
}

@RequestMapping("/accounts")
public String accountList(Model model, HttpSession ses) {
long userId = (Long) ses.getAttribute("user_id");
List<Account> accounts = accountRepository.findUserAccounts(userId);
model.addAttribute("accounts", accounts);
return "accounts";
}

@RequestMapping(value = "/edit/{id}", method = RequestMethod.GET)
public String editAccountForm(Model model, @PathVariable long id) {
Account account = accountRepository.findOne(id);
model.addAttribute("account",account);
return "editAccountForm";
}

@RequestMapping(value = "/edit/{id}", method = RequestMethod.POST)
public String editAccount(@ModelAttribute Account account, @PathVariable long id) {
Account accountToUpdate = accountRepository.findOne(id);
accountToUpdate.setAccTitle(account.getAccTitle());
accountToUpdate.setAccFirstName(account.getAccFirstName());
accountToUpdate.setAccLastName(account.getAccLastName());
accountToUpdate.setAccBirthdate(account.getAccBirthdate());
accountToUpdate.setAccPhoneNumber(account.getAccPhoneNumber());
accountToUpdate.setAccEducation(account.getAccEducation());
accountToUpdate.setAccExperience(account.getAccExperience());
accountToUpdate.setAccAbilities(account.getAccAbilities());
accountToUpdate.setAccInterests(account.getAccInterests());
accountToUpdate.setAccProjects(account.getAccProjects());
accountToUpdate.setAccDescription(account.getAccDescription());
accountRepository.save(accountToUpdate);
return "redirect:/accounts";
}

@RequestMapping("/delete")
public String deleteAccount(Model model) {
return "deleteAccount";
}

@RequestMapping("/read/{id}")
public String read(@PathVariable long id) {
return accountRepository.findOne(id).toString();
}

@RequestMapping("/delete/{id}")
public String delete(@PathVariable long id) {
Account account = accountRepository.findOne(id);
accountRepository.delete(account);
return "redirect:/accounts";
}
}



and last one ImageController:


@Controller
@RequestMapping("/user")
public class ImageController {

private AccountRepository accountRepository;

@RequestMapping(value = "/accounts", method = RequestMethod.GET)
public void showImage(@RequestParam("id") Long id, HttpServletResponse response, HttpServletRequest request)
throws ServletException, IOException {

Account account = accountRepository.getOne(id);
response.setContentType("image/jpeg, image/jpg, image/png, image/gif");
response.getOutputStream().write(account.getAccPicture());

response.getOutputStream().close();
}
}



my .jsp to show account:


<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix = "fmt" uri = "http://java.sun.com/jsp/jstl/fmt" %>
<%@ page isELIgnored="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ include file="/WEB-INF/parts/header.jsp" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<div align="center">
<table class="table table-striped">
<h1>Accounts:</h1>
<c:forEach items="${accounts}" var="account" begin="0" varStatus="theCount">

<tr>
<td>${theCount.index+1}</td>
<td><b>Nazwa: </b>${account.accTitle}</td>
<td><b>Opis: </b>${account.accDescription}</td>
<td><img src="/ProjectProfile/user/accounts?id=${account.id}"/></td>
<td><a style="width: 180px;height: 20px;" href="./edit/${account.id}" class="badge badge-primary">Show/Edit</a></td>
<td><a style="width: 180px;height: 20px;" href="./delete/${account.id}" class="badge badge-danger">Delete</a></td>
</tr>
</c:forEach>
</table>

<a href="<c:url value="/addAccount"/>">Add Account</a>

</body>
</html>



Maybe I need use Base64Encoder, but i don't know how? .... I use pom.xml and AppConfig for configuration. Please, check out this project, maybe somebody can help?





When asking about an exception, always post the exact and complete stack trace of the exception. Also tell us which request causes this exception to happen. Unrelated, but an image has one content type, not 4 different ones.
– JB Nizet
Jun 27 at 12:28





Ok, now i see NullPointerException: SEVERE: Servlet.service() for servlet [dispatcher] in context with path [/ProjectProfile] threw exception [Request processing failed; nested exception is java.lang.NullPointerException] with root cause java.lang.NullPointerException at pl.lukszn.ProjectProfile.controllers.ImageController.showImage(ImageController.java:27) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
– Lukszn
Jun 27 at 12:37






So, look what could be null at line 27 of ImageController. We don't have the line numbers here.
– JB Nizet
Jun 27 at 12:52





Account account = accountRepository.getOne(id);
– Lukszn
Jun 27 at 12:53





So accountRepository is null. Which is quite normal since you didn't inject it properly using the constructor, or using an Autowired annotation.
– JB Nizet
Jun 27 at 12:54




3 Answers
3


<img id="photo" src="data:image/png;base64,${PHOTOYOUNEED}" />



In the controller responsible for the image sending to the html:


(...)
String photoencodeBase64 = modelX.getStringPhoto();
modelAndView.addObject("PHOTOYOUNEED", photoencodeBase64 );



and I also use this method in the model to convert byte to string in base64:


public static String convertBinImageToString(byte binImage) {
if(binImage!=null && binImage.length>0) {
return Base64.getEncoder().encodeToString(binImage);
}
else
return "";
}



and I call it in the getStringPhoto() getter inside the model.





nothing has changed, maybe i do something wrong... add to Account model: private String stringPhoto; and his getter: public String getStringPhoto() { return convertBinImageToString(accPicture); }, next changed ImageController: @RequestMapping(value = "/accounts", method = RequestMethod.GET) public void showImage(@RequestParam("id") long id, Model model) { Account account = accountRepository.findById(id); String photoencodeBase64 = account.getStringPhoto(); model.addAttribute("accPicture", photoencodeBase64); }.... and jsp ...base64,${account.accImage}" />
– Lukszn
Jun 27 at 18:34





can you get the changed main code for the model, controller and jsp using "code sample {}" when posting? Also, what is the error message? Can you also place it here using "code sample {}"? Thanks! :D
– Eunito
Jun 27 at 22:07






i add new answer for your comment
– Lukszn
Jun 28 at 10:20



Ok Eunito, let's see... Changed Account.java(model):


@Entity
@Table(name = "accounts")
public class Account {


@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long id;

private boolean ifBasicAccount;

private String accTitle;

private String accFirstName;

private String accLastName;

private String accBirthdate;

private String accPhoneNumber;

private String accEducation;

private String accExperience;

private String accAbilities;

private String accInterests;

private String accProjects;

private String accDescription;

@Lob
private byte accPicture;

private String stringPhoto;





@ManyToOne
private User user;


public Account() {
}

public long getId() {
return id;
}

public void setId(long id) {
this.id = id;
}

public String getAccTitle() {
return accTitle;
}

public void setAccTitle(String accTitle) {
this.accTitle = accTitle;
}


public String getAccFirstName() {
return accFirstName;
}

public void setAccFirstName(String accFirstName) {
this.accFirstName = accFirstName;
}

public String getAccLastName() {
return accLastName;
}

public void setAccLastName(String accLastName) {
this.accLastName = accLastName;
}

public String getAccBirthdate() {
return accBirthdate;
}

public void setAccBirthdate(String accBirthdate) {
this.accBirthdate = accBirthdate;
}

public String getAccPhoneNumber() {
return accPhoneNumber;
}

public void setAccPhoneNumber(String accPhoneNumber) {
this.accPhoneNumber = accPhoneNumber;
}

public String getAccEducation() {
return accEducation;
}

public void setAccEducation(String accEducation) {
this.accEducation = accEducation;
}

public String getAccExperience() {
return accExperience;
}

public void setAccExperience(String accExperience) {
this.accExperience = accExperience;
}

public String getAccAbilities() {
return accAbilities;
}

public void setAccAbilities(String accAbilities) {
this.accAbilities = accAbilities;
}

public String getAccInterests() {
return accInterests;
}

public void setAccInterests(String accInterests) {
this.accInterests = accInterests;
}

public String getAccProjects() {
return accProjects;
}

public void setAccProjects(String accProjects) {
this.accProjects = accProjects;
}


public String getAccDescription() {
return accDescription;
}

public void setAccDescription(String accDescription) {
this.accDescription = accDescription;
}


public byte getAccPicture() {
return accPicture;
}

public void setAccPicture(byte accPicture) {
this.accPicture = accPicture;
}


public String getStringPhoto() {
return convertBinImageToString(accPicture);
}

public void setStringPhoto(String stringPhoto) {
this.stringPhoto = stringPhoto;
}

public User getUser() {
return user;
}

public void setUser(User user) {
this.user = user;
}

public boolean isIfBasicAccount() {
return ifBasicAccount;
}

public void setIfBasicAccount(boolean ifBasicAccount) {
this.ifBasicAccount = ifBasicAccount;
}

public static String convertBinImageToString(byte accPicture) {
if(accPicture!=null && accPicture.length>0) {
return Base64.getEncoder().encodeToString(accPicture);
}
else
return "";
}

}



I have two controllers for Account(one is only for showing image-I'm not so sure that's a good thing, because i have two the same RequestMappings). So see changed ImageController:


@Controller
@RequestMapping("/admin/user")
public class ImageController {

@Autowired
AccountRepository accountRepository;

@RequestMapping(value = "/accounts", method = RequestMethod.GET)
public void showImage(@RequestParam("id") long id, Model model) {

Account account = accountRepository.findById(id);
String photoencodeBase64 = account.getStringPhoto();
model.addAttribute("accPicture", photoencodeBase64);

}
}



And .jsp to show image:


<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix = "c" uri = "http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix = "fmt" uri = "http://java.sun.com/jsp/jstl/fmt" %>
<%@ page isELIgnored="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ include file="/WEB-INF/parts/header.jsp" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<div align="center">
<table class="table table-striped">
<h1>Accounts:</h1>
<c:forEach items="${accounts}" var="account" begin="0" varStatus="theCount">

<tr>
<td>${theCount.index+1}</td>
<td><b>Title: </b>${account.accTitle}</td>
<td><b>Description: </b>${account.accDescription}</td>
<td><b>Image: </b><img id="photo" src="data:image/png;base64,${account.accPicture}" /></td>
<td><a style="width: 180px;height: 20px;" href="./edit/${account.id}" class="badge badge-primary">Show/Edit</a></td>
<td><a style="width: 180px;height: 20px;" href="./delete/${account.id}" class="badge badge-danger">Delete</a></td>
</tr>
</c:forEach>
</table>

<a href="<c:url value="/addAccount"/>">Add Account</a>

</body>
</html>



so what is happen- when i add new account -> write Title, Name etc. and add image from file my browser show me HTTP Status 400 - The request sent by the client was syntactically incorrect. - > there i need see all user accounts.
In STS console nothing happend. In MySQL too.


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



Why not use Spring Content JPA? This can provide a storage service and rest endpoints for managing content associated with jpa entities.



pom.xml


<!-- Java API -->
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-jpa</artifactId>
<version>0.1.0</version>
</dependency>
<!-- REST API -->
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-rest</artifactId>
<version>0.1.0</version>
</dependency>



Configuration


@Configuration
@EnableJpaStores
@Import("org.springframework.content.rest.config.RestConfiguration.class")
public class MysqlConfig {

// schema management
//
@Value("/org/springframework/content/jpa/schema-drop-mysql.sql")
private Resource dropRepositoryTables;

@Value("/org/springframework/content/jpa/schema-mysql.sql")
private Resource dataRepositorySchema;

@Bean
DataSourceInitializer datasourceInitializer() {
ResourceDatabasePopulator databasePopulator =
new ResourceDatabasePopulator();

databasePopulator.addScript(dropReopsitoryTables);
databasePopulator.addScript(dataReopsitorySchema);
databasePopulator.setIgnoreFailedDrops(true);

DataSourceInitializer initializer = new DataSourceInitializer();
initializer.setDataSource(dataSource());
initializer.setDatabasePopulator(databasePopulator);

return initializer;
}
}



To associate content, add Spring Content annotations to your account entity.



Account.java


@Entity
public class Account {

// replace @Lob field with

@ContentId
private String contentId;

@ContentLength
private long contentLength = 0L;

// if you have rest endpoints
@MimeType
private String mimeType = "text/plain";



Create a "store":



AccountImagesStore.java


@StoreRestResource(path="accountImages)
public interface AccountImagesStore extends ContentStore<Account, String> {
}



This is all you need to create REST endpoints @ /accountImages. When your application starts, Spring Content will look at your dependencies (seeing Spring Content JPA/REST), look at your AccountImagesStore interface and inject an implementation of that interface for JPA. It will also inject a @Controller that forwards http requests to that implementation. This saves you having to implement any of this yourself whch I think is what you are after.


/accountImages


AccountImagesStore


@Controller



So...



curl -X POST /accountImages/{account-id}


curl -X POST /accountImages/{account-id}



with a multipart/form-data request will store the image in the database and associate it with the account entity whose id is account-id.


account-id



curl /accountImages/{account-id}


curl /accountImages/{account-id}



will fetch it again and so on...supports full CRUD.



So all you should need to display this in your JSP is an image tag:





There are a couple of getting started guides here. The reference guide is here. And there is a tutorial video here. The coding bit starts about 1/2 way through.



HTH






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Comments

Popular posts from this blog

paramiko-expect timeout is happening after executing the command

how to run turtle graphics in Colaboratory

Export result set on Dbeaver to CSV