Search code examples
postgresqlgrailsgroovygsp

Grails 2.5.11 / Postgresql 10, Upload Image and Display in GSP


Hi I am new to Grails and PostgreSQL. I am trying to make a form that stores user's data and I want to be able to upload multiple photos.

service:

Cars carInstance = new Cars() 
carInstance.carimg = params.carimg.getBytes()

gsp:

<input type="file" id="carimg" name="carimg" multiple />

and I call saveCar action in controller that save all the data user will input.

And I want to display the data with the image in a showCar gsp this way :

 <img src="${createLink(controller: 'garage', action: 'getImage', params: ['id': Cars.id])}"/>

The getImage action which gets the image and passes it to on to gsp is this :

 def getImage(){
   def item = Cars.get(params.id.toLong())
   byte[] imageInByte=item.carimg
   response.contentType = 'image/png'
   response.outputStream << imageInByte
   response.outputStream.flush() }

In the gsp it appears as a blank border with an image in left corner that means image not found probably. If I convert binary data to string it displays the correct photo name. Any suggestions? The problem is in the way I store the image or the way I try to display a binary data to image?


Solution

  • What is Cars.id in your case? Maybe it is an array of cars and you need to iterate over it?

    <g:each in="${Cars.list()}" var="car">
     <img src="${createLink(controller: 'garage', action: 'getImage', params: ['id': car.id])}"/>
    </g:each>
    

    EDIT 1:

    Based on your responses I create a sample grails(2.5.6) project.

    Domain:

    class Car {
        String name
        byte[] photo
    
        static constraints = {
            photo maxSize: 1024 * 1024 * 2
        }
    }
    

    In Controller I have 2 methods:

    def show(Car carInstance) {
        respond carInstance
    }
    
    def showImage(int id) {
        def item = Car.get(id)
    
        byte[] imageInByte = item.photo
    
        response.contentType = 'image/png'
        response.outputStream << imageInByte
        response.outputStream.flush()
    }
    

    And gsp page with:

    <g:fieldValue bean="${carInstance}" field="name"/>
    <img src="${createLink(controller: 'Car', action: 'showImage', params: [id: carInstance.id])}"/>
    

    And image was rendered successfully.

    EDIT 2:

    Sorry, I miss what you try to do it with multiple images.

    First of all you need to create additional Domain to store photos:

    class CarPhoto {
        byte[] photo
    
        static belongsTo = [car: Car]
    
        static constraints = {
            photo maxSize: 1024 * 1024 * 2
        }
    }
    

    and add this dependencies to Car Domain:

    class Car {
        String name
        static hasMany = [photos: CarPhoto]
    
        static constraints = {}
    }
    

    After that you need to apply those changes to showImage action:

    def showImage(long id, long photo_id) {
        def car = Car.get(id)
        def photo = CarPhoto.findByCarAndId(car, photo_id)
    
        byte[] imageInByte = photo.photo
    
        response.contentType = 'image/png'
        response.outputStream << imageInByte
        response.outputStream.flush()
    }
    

    And to the gsp page:

    <g:each in="${carInstance.photos}" var="photo">
        <img src="${createLink(controller: 'Car', action: 'showImage', params: [id: carInstance.id, photo_id: photo.id])}"/>
    </g:each>
    

    Also you need to change your upload methods. You can find info here.