0

I'm writing a grails (v2.4.4) app to store information on Songs in a music library. One of the "attributes" of a song is the "key". So I want to write a validation/lookup table (domain class) that will hold all valid keys. Since not everyone doing the data entry will automatically know what a staff with 1 Flat is the F Major scale/key I want to include a image of the staff as a comparison

(I can't paste the image of the staff, not enough "points", you can see a staff example in wikepedia if you are interested. http://en.wikipedia.org/wiki/F_major)

I can get this file stored into the MySQL table as a BLOB - grails does that just fine. But I can't for the life of me (using many of the examples I have found) to get the image to "show". All I see in the "Valid Key List" is the string of "bytes". can someone point me in the right direction please.

ValidKeys Domain Class:

package musicdb

class ValidKeys {

    String  musicalKey
    String  aka
    byte[]  staffImg

    static constraints = {
        musicalKey (unique: true, nullable: false)
        aka (nullable: true)
        staffImg (nullable: true, maxSize: (1024*1024))
    }
}

ValidKeysControler: (standard "generated" controller)

package musicdb

import static org.springframework.http.HttpStatus.*
import grails.transaction.Transactional

@Transactional(readOnly = true)
class ValidKeysController {

    static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]

    def index(Integer max) {
        params.max = Math.min(max ?: 10, 100)
        respond ValidKeys.list(params), model:[validKeysInstanceCount: ValidKeys.count()]
    }

    def show(ValidKeys validKeysInstance) {
        respond validKeysInstance
    }

    def create() {
        respond new ValidKeys(params)
    }

    @Transactional
    def save(ValidKeys validKeysInstance) {
        if (validKeysInstance == null) {
            notFound()
            return
        }

        if (validKeysInstance.hasErrors()) {
            respond validKeysInstance.errors, view:'create'
            return
        }

        validKeysInstance.save flush:true

        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.created.message', args: [message(code: 'validKeys.label', default: 'ValidKeys'), validKeysInstance.id])
                redirect validKeysInstance
            }
            '*' { respond validKeysInstance, [status: CREATED] }
        }
    }

    def edit(ValidKeys validKeysInstance) {
        respond validKeysInstance
    }

    @Transactional
    def update(ValidKeys validKeysInstance) {
        if (validKeysInstance == null) {
            notFound()
            return
        }

        if (validKeysInstance.hasErrors()) {
            respond validKeysInstance.errors, view:'edit'
            return
        }

        validKeysInstance.save flush:true

        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.updated.message', args: [message(code: 'ValidKeys.label', default: 'ValidKeys'), validKeysInstance.id])
                redirect validKeysInstance
            }
            '*'{ respond validKeysInstance, [status: OK] }
        }
    }

    @Transactional
    def delete(ValidKeys validKeysInstance) {

        if (validKeysInstance == null) {
            notFound()
            return
        }

        validKeysInstance.delete flush:true

        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.deleted.message', args: [message(code: 'ValidKeys.label', default: 'ValidKeys'), validKeysInstance.id])
                redirect action:"index", method:"GET"
            }
            '*'{ render status: NO_CONTENT }
        }
    }

    protected void notFound() {
        request.withFormat {
            form multipartForm {
                flash.message = message(code: 'default.not.found.message', args: [message(code: 'validKeys.label', default: 'ValidKeys'), params.id])
                redirect action: "index", method: "GET"
            }
            '*'{ render status: NOT_FOUND }
        }
    }
}
cfrick
  • 35,203
  • 6
  • 56
  • 68
mlhart58
  • 1
  • 2
  • I guess I should show how I tried to make this happen. In the controller I added the following code: def displayStaffImage() { def validKeyInstance = File.get(params.id) response.outputStream << validKeyInstance.staffImg response.outputStream.flush() } – mlhart58 Mar 19 '15 at 19:32
  • Tried both of these: and/or G tag did not work, kept record from being stored into the DB – mlhart58 Mar 19 '15 at 19:37

3 Answers3

0

You're almost there. In the displayStaffImage() method you mentioned in your comment, you'll also need to include the image's MIME type as the content type.

For example:

def displayStaffImage() { 
    def validKeyInstance = File.get(params.id) 
    response.setContentType("image/png")
    response.outputStream << validKeyInstance.staffImg
    response.outputStream.flush() 
}

See the accepted answer from Grails: displaying created image in gsp for another example.

If you don't know or aren't storing the MIME type, for whatever reason, you could use content type "application/octet-stream" (indicating a binary file) and specify the filename instead:

def displayStaffImage() { 
    def validKeyInstance = File.get(params.id) 
    response.setContentType("application/octet-stream")
    response.setHeader("Content-disposition", "attachment;filename=\"${validKeyInstance.fileName}\"")
    response.outputStream << validKeyInstance.staffImg
    response.outputStream.flush() 
}

Edit: I note that you aren't actually storing a filename in your key description, so maybe my second suggestion isn't as relevant, but it's still a viable option if you have that information available.

Community
  • 1
  • 1
Chris L
  • 533
  • 1
  • 3
  • 10
0

Thank You Chris.

I actually just got this working in the "show.gsp". I did not have to add the "contentType" to make it work, but I think I will go back and add that domain field and appropriate code since most examples I found seem to include it.

What I did to make it work is:

  1. in the controller "def displayStaffImage()
    • changed the File.get(params.id) to ValidKeys.findById(params.id)
  2. In the "show.gsp" used the tag
    • <span class="property-value" aria-labelledby="staffImg-label"><img src="${createLink(controller:'ValidKeys', action:'displayStaffImage', id:"${validKeysInstance.id}") }" width='100'/></span>
    • BTW, the "span" part is cloned from the other "fields" from the "generate" step. It was not really necessary, but I figured I'd try and be consistent.

However... The same is not working in the "index.gsp" . Id does not like the "id" as a param here. I will try a few more ideas and post if I have success. But if anyone has any pointers for getting the actual image to show in the "index.gsp" I'd be grateful to hear.

Thanks again Chris!

mlhart58
  • 1
  • 2
  • Oops! It works much better without typos! So I now have this working in the index.gsp as well using the same tag without the surrounding "" – mlhart58 Mar 21 '15 at 02:43
0

this worked with me i have this column type in domain

class User implements Serializable {
    Blob userPic
}

and i get the result

 def obj = User.findById(userId)
        def img= obj.userPic
        out << '<img src="data:image/png;base64,'
        out<< img.binaryStream.bytes.encodeBase64()
        out<< '">'
Osama Sbieh
  • 255
  • 1
  • 5
  • 13