5

I have an action that writes directly to the output stream. Sometimes I get the following to errors:

Error processing GroovyPageView: getOutputStream() has already been called for this response
Caused by getOutputStream() has already been called for this response

and this one:

Executing action [getImage] of controller [buddyis.ItemController] caused exception: Runtime error executing action
Caused by Broken pipe

How can I solve these problems? The action I use is listed below.

NOTE: I use Tomcat 7.0.42, if this is important!

def getImage() {
    byte [] imageByteArray = // some image bytes

    response.setHeader 'Content-disposition', "attachment; filename=\"${imageName}${imageExtension}\""
    response.setContentType("image/pjpeg; charset=UTF-8")
    response.contentLength = imageByteArray.size()
    response.outputStream.write(imageByteArray)
    response.outputStream.flush()
    response.outputStream.close()
    return
}
Michael
  • 32,527
  • 49
  • 210
  • 370
  • Try to render something (dummy) to index or render a status code and see if you still get the issue. You can start with `render(status: 200)` or at the least `render "Done"` etc before return. – dmahapatro Aug 06 '13 at 19:56
  • Refer [this question](http://stackoverflow.com/questions/13184603/grails-error-using-httpservletresponse-to-export-file-for-download). – dmahapatro Aug 07 '13 at 00:28

2 Answers2

7

I don't know why you are getting that error, however here is what I do that works everytime.

I don't call .flush() or .close()

response.setContentType("application/octet-stream")
response.setHeader("Content-disposition", "filename=\"${name}\"")
response.setContentLength(imageByteArray.size())
response.outputStream << imageByteArray

Using the above it was working fine, until I found out a user can cancel a download, which caused an exception. This is the full code I use instead of response.outputStream << imageByteArray:

    def outputStream = null
    try {
        outputStream = response.outputStream
        outputStream << imageByteArray

    } catch (IOException e){
        log.debug('Canceled download?', e)
    } finally {
        if (outputStream != null){
            try {
                outputStream.close()
            } catch (IOException e) {
                log.debug('Exception on close', e)
            }
        }
    }
James Kleeh
  • 12,094
  • 5
  • 34
  • 61
  • I use Tomcat 7.0.42, if this is important to the answer? Why ContentType "application/octet-stream"? – Michael Aug 06 '13 at 20:45
  • I think I'm using 7.0.39 so that shouldn't matter. As far as the content type - I allow for download/upload of multiple types of files. This seems to work for all. – James Kleeh Aug 06 '13 at 20:49
  • Okay and what about .flush() and .close() why don't you need that? – Michael Aug 06 '13 at 20:54
  • From my experience Google Chrome arbitrary terminates connections sometimes. So, I would add catching the exception::if (e.getClass().name == 'org.apache.catalina.connector.ClientAbortException') { log.warn "Request was arbitrary terminated by the client: $e.cause" } – Andriy Budzinskyy Aug 06 '13 at 21:40
  • What @Andiry added was a feature of logging - its personal preference. It does not change the way it works. Answers should have the least amount of information possible to sufficiently answer the question. – James Kleeh Aug 07 '13 at 04:30
0

I had this issue whilst running grails 2.5 on tomcat:7.0.55.3 and with the java-melody grails plugin installed. After uninstalling java-melody it worked just fine

Jeowkes
  • 501
  • 7
  • 20