How to upload files in spring boot into the database

In this article, we are going to demonstrate the file upload using HTML form and will store it in the database. Let's follow the steps.

You may be interested to know --> How to upload files to the database

How to upload image file in database in spring boot

 

Step 1. Set file upload size limit in the properties file

#......
spring.servlet.multipart.enabled
=true
spring.servlet.multipart.max-file-size=2MB
spring.servlet.multipart.max-request-size=2MB

Step 2. HTML form

<form method="post" action="/uploadFile" enctype="multipart/form-data">
   <input type="file" name="myfile" accept="image/*">
   <input type="submit" value="Upload">
</form>

Here, in the form tag  enctype="multipart/form-data" is must to send the files and other textual data to the server, if you do not apply, the controller will reject with the exception:

MultipartException: Current request is not a multipart request

And in the input type file tag, accept="image/" tells that it will accept only image files, we can apply other checks like audio/*, video/*, etc. and whatever we required.

 

Step 3. Create the Entity to save image details

// we have used lombok dependency for getter and setter annotation
@Getter
@Setter
@Entity
public class ImageGallary {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String type;
    @Lob
    private byte[] fileByte;

//... other fields
}

 

 

Step 4. Create the JPA repository for the entity

public interface ImageRepository  extends JpaRepository<ImageGallary,Long> {
    Optional<ImageGallary> findByName(String filename);
//...other methods
}

 

Step 5. A controller method to upload files

   @Autowired
    private ImageRepository imageRepository;

    @PostMapping("/uploadFile")
    public String uploadFile(@RequestParam("myfile") MultipartFile myfile) throws IOException {
        System.out.println("Image Size in Bytes - " + myfile.getBytes().length);
        ImageGallary img = new ImageGallary();
        img.setName(myfile.getOriginalFilename());
        img.setType(myfile.getContentType());
        img.setFileByte(myfile.getBytes());
        imageRepository.save(img);
        return "redirect:/";
    }

 

Step 6.  Method to retrieve the image file

Since we have stored the file in bytes, it cannot be displayed directly in HTML UI, so either we write the bytes into the HTTP response object or convert in Base64 string.

Method 1. Writing the byes to HTTP response object

 Make to controller method to get a single file by getting the name in the path. like http://localhost:8080/images/filename.jpg

    @GetMapping("/images/{filename}")
    public void getImage(@PathVariable("filename") String filename, HttpServletResponse response) throws Exception {
        Optional<ImageGallary> img = imageRepository.findByName(filename);
        if(img.isPresent()) {
            System.out.println("returning file:"+filename);
            byte[] bytes = img.get().getFileByte();
            InputStream is = new BufferedInputStream(new ByteArrayInputStream(bytes));
            String mimeType = URLConnection.guessContentTypeFromStream(is);
            response.setContentType(mimeType);
            OutputStream outputStream = response.getOutputStream();
            outputStream.write(bytes);
            outputStream.flush();
            outputStream.close();
        }
    }

 

And look at the sample to render files.

Method mapping :

 @GetMapping("/")
    public String home(Model model){
        List<ImageGallary> images = imageRepository.findAll();
        model.addAttribute("images",images);
        return "index";
    }

and  in HTML file ( we are using thymeleaf dependency)

<!-- we have used bootstrap CDN to display in row and columns --> 
 <div class="row">
        <div class="col-md-3" th:each="img:${images}">
            <div class="container">
                <p th:text="${img.name}"></p>
                <p th:text="${img.type}"></p>
                <img th:src="${'/images/'+img.name}" style="max-height: 110px">
            </div>
        </div>
    </div>

 

Method 2. Converting the bytes into the Base64 string

To do this we need to add a transient field in the entity and set the Base64 string before setting it to the model attributes or returning it to the browser/client. Why transient? Transient fields are not saved in the database. Then the question may arise in your mind, instead of saving the file in byte format why can't we save the base64 string into the database? The answer is, yes you can save but the database engine may through MysqlDataTruncation: Data truncation: Data too long for column.. then we need to apply @Column(columnDefinition = "LONGTEXT") over the field the data truncation exception can be solved. There are many ways to do the same task, it's up to you which one you choose. Here we have already saved the images in byte array so we take a transient field. You may move this field to DTO if you are using the DTO concept.

@Entity
public class ImageGallary {
//.. other fields are shown in step 2
    @Transient
    private String base64Img;
}

changes required in the controller is

  @GetMapping("/")
    public String home(Model model){
        List<ImageGallary> images = imageRepository.findAll();
        images.forEach(img->{
            img.setBase64Img("data:image/png;base64,"+ Base64.encode(img.getFileByte()));
        });
        model.addAttribute("images",images);
        return "index";
    }

and changes in the Html page

 <div class="row">
        <div class="col-md-3" th:each="img:${images}">
            <div class="container">
                <p th:text="${img.name}"></p>
                <p th:text="${img.type}"></p>
                <img th:src="${img.base64Img}" style="max-height: 110px">
                <!-- <img th:src="${'/images/'+img.name}" style="max-height: 110px">-->
            </div>
        </div>
    </div>

Output Screen

file upload to datbase

 

Advantages of Storing files into database

  1. It is easier to replicate to other servers and create backups.
  2. Less code to handle saving, updating, and deleting the file.
  3. It is easier to associate the file with other data in your database.
  4. You do not need to worry about consistency (e.g. deleting the file but still having the database record, or deleting the database record but still having the file). Instead, it allows foreign key constraints and database transactions to take care of the details.
  5. In certain databases (such as Oracle and SQL Server) you can index files and search within them using SQL

Disadvantages of storing files into database

  1. Read/Write to a DB is always slower than a file system.
  2. Can lead to large databases. Care needs to be taken when selecting binary columns.
  3. Database Backups will become more hefty and heavy, which makes restoration & replication slower. (It's a problem where daily size is in TB’s)

 

My Opinion

My opinion is, it depends upon the requirements and conditions.

If we have small file size like passport size photograph or signature under 100kb. then suggest storing it in the database.

If we have to store the large-sized files and a large number of files then we must use any of the files system, CDN, Amazon S3, Firebase Storage, etc.

Conclusion:

What we have learned in the above steps? We received the image as a MultipartFile object from the HTML form and saved it in a database in a bytes array format using @Lob annotation, and retrieved using response object and base64 image format. And we have seen its advantages and disadvantages

Thanks for reading this article, I hope you understand well the above explanation.

And as always you can find the source code on GitHub

 


×