{getToc} $title={Table of Contents}
Spring Boot: Image Upload |
Introduction
In this article, we will implement the file upload feature. We will create two
endpoints which will upload and get the files respectively. The post request
will upload the file. Post request can also be used as a put request.
The get request will get the file.
Body
Project Structure in Spring Tool Suite
pom.xml file
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.1</version> <relativePath /> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>UploadImage</artifactId> <version>0.0.1-SNAPSHOT</version> <name>UploadImage</name> <description>SprintBoot App for upload feature</description> <properties> <java.version>11</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>com.fasterxml.uuid</groupId> <artifactId>java-uuid-generator</artifactId> <version>4.0.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
application.yml
In this application yml, we have created with Database connection by using
postgresql database.
spring: jpa: hibernate: ddl-auto: update naming: physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl properties: hibernate: format_sql: true jdbc.lob.non_contextual_creation: true dialect: org.hibernate.dialect.PostgreSQLDialect show_sql: true defer-datasource-initialization: true datasource: url: jdbc:postgresql://localhost:5432/image-upload-springboot username: postgres password: 123456 logging: level: org.springframework: ERROR com.example.UploadImage: DEBUG
UploadImageApplication class
package com.example.UploadImage; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class UploadImageApplication { public static void main(String[] args) { SpringApplication.run(UploadImageApplication.class, args); } }
UserController class
This is the controller class. We have two endpoints:
- Post Endpoint : The post request will upload the file.
- Get Endpoint : The get request will get the file.
package com.example.UploadImage.controller; import com.example.UploadImage.entity.ProfileUser; import com.example.UploadImage.service.UserService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @Slf4j @RestController @RequestMapping("users") public class UserController { @Autowired UserService userService; @PostMapping("/upload/post") public ResponseEntity<String> uploadProfilePic(@ModelAttribute ProfileUser user) throws Exception { log.debug("Inside {} controller", "/users/upload/post"); userService.updateProfilePicture(user.getUserId(), user.getImageFile()); return new ResponseEntity<>( "Upload Successful", HttpStatus.OK); } @GetMapping(value = { "/upload/get" }, produces = MediaType.ALL_VALUE) public ResponseEntity<byte[]> getProfilePic(@RequestParam String emailId) { log.debug("Inside {} controller", "/users/upload/get"); ProfileUser user = userService.getUserDetailsByEmailId(emailId); byte[] profilePicBytes = user.getImageByte(); return new ResponseEntity<>(profilePicBytes, HttpStatus.OK); } }
ProfileUser Entity class
We have created the profile user entity class which have
: userId, emailId and imageFile for
requesting data.
package com.example.UploadImage.entity; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.hibernate.annotations.Cache; import org.hibernate.annotations.CacheConcurrencyStrategy; import org.hibernate.annotations.ColumnTransformer; import org.hibernate.annotations.GenericGenerator; import org.springframework.web.multipart.MultipartFile; import javax.persistence.*; @Data @AllArgsConstructor @NoArgsConstructor @Entity @Table(name = "profileuser") @Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE) public class ProfileUser { @Id @Column(name = "userId") @GeneratedValue(generator = "uuid") @GenericGenerator(name = "uuid", strategy = "uuid2") private String userId; @Column(columnDefinition= "VARCHAR", name="emailId") private String emailId; @JsonIgnore @Column(name="imageByte") private byte[] imageByte; @Transient private MultipartFile imageFile; }
We have used
@Table(name = "profileuser")
This will create table profileuser with userId,
emailId & imageByte columns.
imageFile is just used to read the file which is passed while calling
the post request. We convert this file to a byte type variable and store this
array of bytes in our db.
During get call, we read these array of bytes and pass this to frontend, so
that the bytes are converted to image. For the conversion we use :
produces = MediaType.ALL_VALUE
while defining the “/upload/get” controller. If we know the the MediaType
produced by our get endpoint, then we can use a specific mediatype. Like :
produces = MediaType.IMAGE_JPEG_VALUE
UserService Interface
We have created user service interface for upload and get file method using
Multipart File. And injected that service in Upload Image Controller class.
package com.example.UploadImage.service; import com.example.UploadImage.entity.ProfileUser; import org.springframework.web.multipart.MultipartFile; public interface UserService { void updateProfilePicture(String id, MultipartFile multipartFile) throws Exception; ProfileUser getUserDetailsByEmailId(String emailId); }
UserServiceImpl class
This service implementation class is used to implement service method. In this
class we have injected the repository interface too.
package com.example.UploadImage.serviceimpl; import com.example.UploadImage.entity.ProfileUser; import com.example.UploadImage.repository.UserRepository; import com.example.UploadImage.service.UserService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; @Slf4j @Service public class UserServiceImpl implements UserService { @Autowired UserRepository userRepository; @Override public void updateProfilePicture(String userId, MultipartFile multipartFile) throws Exception { ProfileUser user = userRepository.findByUserId(userId); if(user!= null && multipartFile!=null) { user.setImageByte(multipartFile.getBytes()); userRepository.save(user); } else { log.debug("User not found for userId : {}", userId); throw new Exception("User not found for { userId = " + userId + "}"); } } @Override public ProfileUser getUserDetailsByEmailId(String emailId) { ProfileUser user = userRepository.findByEmailId(emailId); return user; } }
UserRepository Interface
We created upload file and get file to do transaction with database in this
repository interface.
In this interface we have extended the JpaRepository interface;
This interface need to be injected by service implemtation class.
package com.example.UploadImage.repository; import com.example.UploadImage.entity.ProfileUser; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; @Repository public interface UserRepository extends JpaRepository<ProfileUser, String> { ProfileUser findByEmailId(String email); ProfileUser findByUserId(String id); }
Build Project
[INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 25.277 s [INFO] Finished at: 2022-08-19T15:49:14+07:00 [INFO] ------------------------------------------------------------------------
Auto create table
After build project successfully, then the table will be auto created.
Create Database with PgAdmin
I have created database name image-upload-springboot as set in the
application yml.
image-upload-springboot |
Run Project
Go to parent project package -> Right click Run As -> Spring Boot App:
Note: Before you can create post request to upload profile image, you
need to insert new profile user infomation with userId and email direct in
table.
Due to our post controller for update user image profile by userId, so we need
to have user info with userId first.
In this article, we're not implement on create user profile info, we
just focus on upload file feature only.
How to call these endpoints from postman?
Post request : /users/upload/post: http://localhost:8080/users/upload/post
Get User Profile Info
Get request : /users/upload/get: http://localhost:8080/users/upload/get
Due to my image uploaded is png, then I will change the mediaType my get
endpoint in controller like:
produces = MediaType.IMAGE_PNG_VALUE
MultipartFile object has a method called “getBytes()”. This method is used to
convert the file to bytes, which can then be stored in the DB. In the below
code we just read the file in bytes, and then set it to
imageByte (byte[] type object) column/variable of our
ProfileUser Entity.
ProfileUser user = userRepository.findByUserId(userId); if(user!= null && multipartFile!=null) { user.setImageByte(multipartFile.getBytes()); userRepository.save(user); } else { log.debug("User not found for userId : {}", userId); throw new Exception("User not found for { userId = " + userId + "}"); }
@ModelAttribute :
In the post request “/users/upload/post” we are using
@ModelAttribute
instead of @RequestBody
-the
@ModelAttribute
will take a query string. so, all the data are
being passed to the server through the url. As for
@RequestBody
, all the data will be passed to the server through
a full JSON body. Image upload wouldn’t be possible with
@RequestBody
.
Multipart :
Multipart requests combine one or more sets of data into a single body,
separated by boundaries. You typically use these requests for file uploads
and for transferring data of several types in a single request (for example,
a file along with a JSON object).
Conclusion
Hope this article was helpful.