Grails:在gsp中显示创建的图像

14

我对Grails非常陌生,所以这个问题可能很简单。我试图在gsp中显示一个动态创建的图像,该图像并没有存储在数据库中,而是在Controller中实时生成。

我的一个gsp页面(requestGraph.gsp)包含一个表单,其中用户输入一组参数。提交表单后,这些参数将被发送到控制器中的displayGraph action,该action从完全独立于Grails的数据库中查询信息,并使用JFreeChart库创建图表。我希望将此图像显示在displayGraph.gsp或类似的页面中。

因此,基本上在requestGraph.gsp中,我有一个类似于以下代码片段:

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

在控制器中我有这样一段:

def requestGraph = {}

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

在displayGraph.gsp文件中:

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

我尝试直接将图像传输到displayGraph操作的输出流中。这样做可以成功,但我失去了控制displayGraph.gsp页面格式的能力。

我发现大多数教程都会创建一个专门的操作来将图像传输到输出流,然后使用 <img> 标签调用该操作。问题是,我的图像不存储在数据库中,我看不到传递图像字节数组(甚至表单参数)以创建/呈现图像的方法。有人能帮帮我吗?谢谢。

8个回答

11

如果你将这些字节写入输出流,就可以将控制器/操作视为 GSP 中图像的源。这是一个快速的、未经测试的示例:

// 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()
}

你可以通过以下方式在 <img> 标签的 src 属性中访问你的图片:

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

更新:

重新阅读您的问题后,这种方法可能行得通也可能行不通——看起来您可能正在通过表单提交来显示图表。只有在服务器上存储状态(而不是仅从提交表单的一个请求中获取状态)时,此方法才能起作用。如果您在服务器上存储了足够的状态以生成图表,则必须为控制器提供一些附加参数以获得正确的图像,例如 src="${g.link(controller: 'myController', action: 'displayGraph', params: ['id': 1234])}",其中id是检索图表状态的方式。


我已经尝试过了。我发现当我直接将图像写入输出流时,它会立即在浏览器中显示为图像而不是网页。这当然意味着我无法向页面添加任何类型的格式或逻辑。 - Klam
这是如果你直接在浏览器中访问“myController/displayGraph”会发生的事情;但如果你将其用作图像的“src”,你仍然拥有页面控制。然而,我刚刚更新了我的答案,并附有一些限制,可能仍会对你造成阻碍。 - Rob Hruska
这是正确的做法。但我建议您将图像和其他文件保存在文件系统中,仅在数据库中存储路径和类型。附言:如果您需要调整图像大小的代码(这对我的项目很有用),请问我。 - Stan
谢谢。 我最终将表单中的所有参数聚合到一个命令对象中,并将其存储在闪存范围中。 然后,使用Rob的方法,我从gsp链接到一个操作,该操作将检索命令对象并创建图像。 感觉有点hacky,但它可以工作。 - Klam
对我来说, response.outputStream << img 不起作用。我不得不使用 response.outputStream.write(img) - 只是提醒其他遇到此问题的人注意一下。 - andy mccullough

3
以下代码适用于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>

3
我认为这不是关于Grails,而是关于HTML。你可以:
  1. 创建一个专门的操作,使其接受某些参数,并在该操作中生成图像;
  2. 将其base64编码嵌入到HTML中,例如这里

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


这可能是最优雅的答案,因为现在大多数浏览器都支持它。然而,如果他必须支持IE7,这将不起作用。 - Rob Hruska

2

对@Rob的回答进行了改进:

另一个更好的选择是,您可以直接从控制器的操作中呈现图像:

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

更多信息请参见:http://grails.org/doc/latest/ref/Controllers/render.html


2

我的建议实际上包含两个部分。

1) 使用Rob上面推荐的解决方案生成图表工件,但要将其保存为带有唯一标识符的静态文件,该标识符作为表单提交响应的一部分返回,然后呈现图表就与呈现任何其他图像没有区别。

我建议标识符是从在表单提交中传递的特定参数构建的,这样它们可以用作缓存机制,在不重新构建图表的情况下再次呈现它们。

2) 创建一个服务,可能是一个Quartz服务,定期清除为应用程序创建的静态图表。


0

由于某些原因,上述解决方案无法显示任何图像。我使用了:

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

instead.


0

我在Grails中使用了FileUploadService来保存图像文件。如果你是从目录中获取图片,可以尝试以下方法:

<img style="height: 120px;width: 102px;"src="${resource(dir:'personImages', file:    personalDetailsInstance.id + '.png')}" />

0

您的GSP:

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

你的控制器:

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()
  }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接