14

I'm very new to Grails so there's probably a very simple answer to this question. I'm trying to display a dynamically created image in a gsp. The image is NOT stored in a database, it's created on the fly in the controller.

What I essentially have is one gsp that has a form which takes in a set of user inputs (requestGraph.gsp). Upon submitting the form, the parameters are sent to the a displayGraph action in the controller which queries information from a database completely outside of Grails and creates a chart using the JFreeChart library. I would like to display this image within a displayGraph.gsp or something like that.

So basically within requestGraph.gsp I have a snippet similar to:

<g:form action="displayGraph">
    <!-- ... bunch of labels and boxes -->
    <g:submitButton name="displayGraph" value="Display Graph" />
</g:form>

Within the controller I have something like:

def requestGraph = {}

def displayGraph = {
    //... code that uses params  to make an image byte array and assigns to var img
    return [image : img]
}

Within displayGraph.gsp:

<body>
    <h1>Graph Title</h1>
    <!-- ??? How to dislpay image? -->
</body>

I tried piping the image directly to the output stream in the displayGraph action. This works, but then I lose control of all page formatting in displayGraph.gsp.

Most tutorials I've found create a dedicated action to pipe the image to an output steam then call that action using a tag. The problem is that my image isn't stored in a database and I see no way of passing the image byte array (or even the form parameters) to create/render the image. Can anybody help me with this? Thanks.

Victor Sergienko
  • 13,115
  • 3
  • 57
  • 91
Klam
  • 505
  • 1
  • 5
  • 11

8 Answers8

11

If you write the bytes to the output stream, you can treat the controller/action as the source of the image in your GSP. Here's a quick, untested example:

// controller action
def displayGraph = {
    def img // byte array
    //...
    response.setHeader('Content-length', img.length)
    response.contentType = 'image/png' // or the appropriate image content type
    response.outputStream << img
    response.outputStream.flush()
}

You could then access your image in the src of an <img> tag like this:

<img src="${createLink(controller: 'myController', action: 'displayGraph')}"/>

Update:

After reading your question again, this may or may not work - it looks like you might be displaying the graph as the result of a form submission. This will only work if you're storing the state on the server somewhere (instead of just getting it from the one request where the form is submitted). If you do store enough state on the server to generate the graph, then you'd have to provide some additional parameters to your controller to get the correct image, e.g. src="${g.link(controller: 'myController', action: 'displayGraph', params: ['id': 1234])}", where id is how you retrieve the graph state.

Rob Hruska
  • 118,520
  • 32
  • 167
  • 192
  • I've tried that. I find that when I write the image directly to the output stream, it's immediately displayed in the browser as an image instead of a web page. This of course, means I can't add any type of formatting or logic to the page. – Klam Dec 21 '10 at 20:25
  • That's what happens if you go directly to `myController/displayGraph` in your browser; but if you use that as the `src` of an image, you still have page control. However, I just updated my answer with a stipulation that may still impede you. – Rob Hruska Dec 21 '10 at 20:26
  • This is right way to do this. But I advise you save images and other files in file system and store in db only path and type. P.S. If you want code for resizing image(this was useful in my proj), ask me. – Stan Dec 21 '10 at 22:48
  • Thanks. I ended up aggregating all parameters from my form into a command object and storing it in the flash scope. Using Rob's method, I then linked to an action from the gsp which would retrieve the command object and create the image. Feels a little hacky, but it works. – Klam Dec 22 '10 at 20:44
  • For me the `reponse.outputStream << img` didnt work. I had to put : `response.outputStream.write(img)` - just a heads up for anyone else with this problem – andy mccullough Sep 29 '13 at 15:50
3

The following code works in Grails 2.x.

HomeController.groovy

class HomeController {

    def index() {
    }


    def viewImage(){
        def file = new File(params.title)
        def img = file.bytes
        response.contentType = 'image/png' // or the appropriate image content type
        response.outputStream << img
        response.outputStream.flush()
    }
}

views/home/index.jsp

<%@ page contentType="text/html;charset=ISO-8859-1" %>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"/>
    <meta name="layout" content="main"/>
    <title>View Image</title>
</head>
<body>
  <div class="body">
  <img src="<g:createLink controller="home" action="viewImage"
          params="[title: 'C:/pictures/myimage.png']"/>"/>
  </div>
</body>
</html>
John Doe
  • 31
  • 1
3

I believe it's not about Grails but about HTML. You could:

  1. Make the dedicated action that pipes image accept certain parameters, and generate the image in that action;
  2. Embed it base64-encoded into HTML, like here:

    <img src="data:image/gif;base64,R0lGODlhUAAPAKIAAAsL...etc..." alt="wow" />

Victor Sergienko
  • 13,115
  • 3
  • 57
  • 91
  • This is probably the most elegant answer, since most browsers support this nowadays. However, if he has to support IE7, this won't work. – Rob Hruska Dec 21 '10 at 20:28
2

Just an improvement to @Rob's answer:

Another option which is a bit nicer, you can just render the image, straight from your controller's action:

// controller action
def displayGraph = {
    def img // a byte[], File or InputStream
    render(file: img, contentType: 'image/png')
}

See also: http://grails.org/doc/latest/ref/Controllers/render.html

Nick Grealy
  • 24,216
  • 9
  • 104
  • 119
2

my suggestion actually has two parts.

1) Use the solution recommend by Rob above to generate the chart artifact, however save it to a static file with a unique identifier that is passed back as part of the response from the form submit, then rendering the chart is no different then rendering any other image.

i would suggest that the identifer be constructed from the specifif params passed in on the form submit so they can be used as a caching mechanism to render the chart again without rebuilding it

2) create a service, maybe a Quartz Service, that periodically cleans up the static charts that were created for the application

Aaron Saunders
  • 33,180
  • 5
  • 60
  • 80
0

I used FileUploadService to save the Image file In Grails .If you getting images from directory means, try this:

<img style="height: 120px;width: 102px;"src="${resource(dir:'personImages', file:    personalDetailsInstance.id + '.png')}" />
Undo
  • 25,519
  • 37
  • 106
  • 129
0

Your gsp:

<img src="${createLink(controller: "image", action: "draw")}" />

Your controller:

def draw() {
  byte[] imageInBytes = imageService.getImageInBytes() //should return byte array 

  response.with{
    setHeader('Content-length', imageInBytes.length.toString())
    contentType = 'image/jpg' // or the appropriate image content type
    outputStream << imageInBytes
    outputStream.flush()
  }
}
Anton Hlinisty
  • 1,441
  • 1
  • 20
  • 35
0

For any reasons the above solutions does not display any image. I used:

<img src='${createLink(controller: "myController", action: "displayGraph")}' />

instead.

Bob
  • 1