Many web applications allow users to store images for later. For example, you may want to allow users to upload a profile picture that gets displayed later on in the application. This post demonstrates how to upload an image to a web application and store the image in a database. Then we will see how to display that image in a browser.
PersistedImage
The key to storing an image in a database is to use @Lob annotation in JPA and make the datatype as a byte array. Here is an example class that stores byte array in the database.
@Entity data class PersistedImage(@field: Id @field: GeneratedValue var id : Long = 0, //The bytes field needs to be marked as @Lob for Large Object Binary @field: Lob var bytes : ByteArray? = null, var mime : String = ""){ fun toStreamingURI() : String { //We need to encode the byte array into a base64 String for the browser val base64 = DatatypeConverter.printBase64Binary(bytes) //Now just return a data string. The Browser will know what to do with it return "data:$mime;base64,$base64" }
Kotlin has a ByteArray class. In Java you would use byte []
. The effect is the same either way. When persistence provider scans this class, it will store the byte array as a Lob in the database. Nevertheless it’s not enough to simply store an image in the database. At some point in time, the user will most likely wish to see the image. That’s there the toStreamingURI() method comes in handy.
The first line uses DatatypeConverter to convert the byte array to a base64 string. Then we can append that string to “data:[mime];base64,[base 64]”. In our example, we use Kotlin’s String template feature to build such a String. We start with the data: followed by the mime (such as /img/png). Then we can add the base64 string created by DatatypeConverter. This string can get added to the src attribute of the html img tag as shown in the screen shot below.
The browser knows how to display this string as an image.
File Uploads
It’s worth while to discuss how files are upload in Spring. Spring has a MultipartFile class that can get mapped to the a file upload input tag in the form. Here is how it looks in the HTML code.
There are a couple of things that are critical for this to work. First, we have to set our applications.properties file to allow large file uploads.
spring.http.multipart.max-file-size=25MB spring.http.multipart.max-request-size=25MB
Next our form tag has to set the enctype attribute to “multipart/form-data”. Finally we have to keep track of the name attribute on our input tag so that we can map it to the server code. In our example, our input tag has it’s name attribute set to “image”.
On the server end, we use this code get an instance of MultipartFile.
@RequestMapping(method = arrayOf(RequestMethod.POST)) fun doPost( //Grab the uploaded image from the form @RequestPart("image") multiPartFile : MultipartFile, model : Model) : String { //Save the image file imageService.save(multiPartFile.toPersistedImage()) model.addAttribute("images", imageService.loadAll()) return "index" }
We annotate the multipartFile parameter with @RequestPart and pass to the annotation the same name attribute that we set on our input tag. At this point, the container will inject an instance of MultipartFile that represents the file that the user uploaded to the server. The MultipartFile class has two attributes that are critical to our purposes. First it has a byte array property that represents the bytes of the uploaded file and it has the file’s MIME.
We can use Kotlin’s extension functions to add a toPersistedImage() method on MutlipartFile.
fun MultipartFile.toPersistedImage() = PersistedImage(bytes = this.bytes, mime = this.contentType)
This method simply returns an instance of PersisitedImage that can get stored in the database. At this point, we can easily store and retrieve the image from the database.
Application
The demonstration application is a regular Spring MVC application written in Kotlin. You can refer to this post on an explanation on how this works. Here is the Kotlin code followed by the HTML code.
Kotlin Code
package com.stonesoupprogramming.streamimage import org.hibernate.SessionFactory import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.SpringApplication import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.stereotype.Controller import org.springframework.stereotype.Repository import org.springframework.stereotype.Service import org.springframework.ui.Model import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestMethod import org.springframework.web.bind.annotation.RequestPart import org.springframework.web.multipart.MultipartFile import javax.persistence.* import javax.transaction.Transactional import javax.xml.bind.DatatypeConverter @SpringBootApplication class StreamImageDbApplication fun main(args: Array) { SpringApplication.run(StreamImageDbApplication::class.java, *args) } @Entity data class PersistedImage(@field: Id @field: GeneratedValue var id : Long = 0, //The bytes field needs to be marked as @Lob for Large Object Binary @field: Lob var bytes : ByteArray? = null, var mime : String = ""){ fun toStreamingURI() : String { //We need to encode the byte array into a base64 String for the browser val base64 = DatatypeConverter.printBase64Binary(bytes) //Now just return a data string. The Browser will know what to do with it return "data:$mime;base64,$base64" } } //This is a Kotlin extension function that turns a MultipartFile into a PersistedImage fun MultipartFile.toPersistedImage() = PersistedImage(bytes = this.bytes, mime = this.contentType) @Configuration class DataConfig { @Bean fun sessionFactory(@Autowired entityManagerFactory: EntityManagerFactory) : SessionFactory = entityManagerFactory.unwrap(SessionFactory::class.java) } @Repository class ImageRepository(@Autowired private val sessionFactory: SessionFactory){ fun save(persistedImage: PersistedImage) { sessionFactory.currentSession.saveOrUpdate(persistedImage) } fun loadAll() = sessionFactory.currentSession.createCriteria(PersistedImage::class.java).list() as List } @Transactional @Service class ImageService(@Autowired private val imageRepository: ImageRepository){ fun save(persistedImage: PersistedImage) { imageRepository.save(persistedImage) } fun loadAll() = imageRepository.loadAll() } @Controller @RequestMapping("/") class IndexController(@Autowired private val imageService: ImageService){ @RequestMapping(method = arrayOf(RequestMethod.GET)) fun doGet(model : Model) : String { model.addAttribute("images", imageService.loadAll()) return "index" } @RequestMapping(method = arrayOf(RequestMethod.POST)) fun doPost( //Grab the uploaded image from the form @RequestPart("image") multiPartFile : MultipartFile, model : Model) : String { //Save the image file imageService.save(multiPartFile.toPersistedImage()) model.addAttribute("images", imageService.loadAll()) return "index" } }
application.properties
spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.properties.hibernate.current_session_context_class=org.springframework.orm.hibernate5.SpringSessionContext spring.datasource.driver-class-name=org.hsqldb.jdbcDriver spring.http.multipart.max-file-size=25MB spring.http.multipart.max-request-size=25MB
index.html
Conclusion
Spring and Kotlin make it easy to embed images in a database and display those images in a browser. The main take away is to define a byte array property as a Lob on persisted image and then convert it to a base64 String when you wish to display it. Here are some screen shots of the working application.
You can get the source code for this project at my GitHub here or watch the video tutorial on YouTube.
One thought on “Kotlin Stream Image from Database”