I have a groovy/grails application that needs to serve images
It works fine on my dev box, the image is returned properly. Here's the start of the returned JPEG, as seen by od -cx
0000000  377 330 377 340  \0 020   J   F   I   F  \0 001 001 001 001   ,
             d8ff    e0ff    1000    464a    4649    0100    0101    2c01
but on the production box, there's some garbage in front, and the d8ff e0ff before the 1000 is missing
0000000    ?  **  **   ?  **  **   ?  **  **   ?  **  **  \0 020   J   F
             bfef    efbd    bdbf    bfef    efbd    bdbf    1000    464a
0000020    I   F  \0 001 001 001  \0   H  \0   H  \0  \0   ?  **  **   ?
             4649    0100    0101    4800    4800    0000    bfef    efbd
It's the exact same code. I just moved the .war over and run it on a different machine. (Isn't Java supposed to be write once, run everywhere?)
Any ideas? An "encoding" problem?
The code is sent to the response like this:
   response.contentType = "image/jpeg"; response.outputStream << out;
Here's the code that locates the image on an internal application server and re-serves the image.  I've pared down the code a bit to remove the error handling, etc, to make it easier to read.
def show = {
    def address = "http://internal.application.server:9899/img?photoid=${params.id}"
    def out = new ByteArrayOutputStream()
    out << new URL(address).openStream()    
    response.contentLength = out.size();
    // XXX If you don't do this hack, "head" requests won't work!
    if (request.method == 'HEAD')
    { render( text : "", contentType : "image/jpeg" ); }
    else {
        response.contentType = "image/jpeg"; response.outputStream << out;
    }
}
Update: I tried setting the CharacterEncoding
response.setCharacterEncoding("ISO-8859-1");
if (request.method == 'HEAD')
{ render( text : "", contentType : "image/jpeg" ); }
        else {
            response.contentType = "image/jpeg;charset=ISO-8859-1";  response.outputStream << out;
 }
but it made no difference in the output. On my production machine, the binary bytes in the image are re-encoded/escaped as if they were UTF-8 (see Michael's explanation below). It works fine on my development machine.