Output binary data from CGI in Python 3
Asked Answered
S

1

11

This question is related to this one. I was having no problems while printing raw binary data from a CGI script in Python 2, for example:

#!/usr/bin/env python2

import os

if __name__ == '__main__':
    with open(os.path.abspath('test.png'), 'rb') as f:
        print "Content-Type: image/png\n"
        print f.read()

Here are the relevant response headers:

> GET /cgi-bin/plot_string2.py HTTP/1.1
> User-Agent: curl/7.32.0
> Host: 0.0.0.0:8888
> Accept: */*
> 
* HTTP 1.0, assume close after body
< HTTP/1.0 200 Script output follows
< Server: SimpleHTTP/0.6 Python/3.3.2
< Date: Fri, 13 Sep 2013 16:21:25 GMT
< Content-Type: image/png

and the result is interpreted as an image, as expected. However, if I try to make a translation to Python 3:

#!/usr/bin/env python

import os
import sys

if __name__ == '__main__':
    with open(os.path.abspath('test.png'), 'rb') as f:
        print("Content-Type: image/png\n")
        sys.stdout.buffer.write(f.read())

Nothing is returned, and here are the headers:

> GET /cgi-bin/plot_string3.py HTTP/1.1
> User-Agent: curl/7.32.0
> Host: 0.0.0.0:8888
> Accept: */*
> 
* HTTP 1.0, assume close after body
< HTTP/1.0 200 Script output follows
< Server: SimpleHTTP/0.6 Python/3.3.2
< Date: Fri, 13 Sep 2013 16:22:13 GMT
< �PNG
< 

I cannot do print(f.read()) anymore because that will print something like b'\x89PNG\r\n\x1a\n\x00.... The question I linked gives a solution, but apparently doesn't work in this environment.

Thoughts?

Added: Note for the future:

This also means print is no good for CGI any more.

Starks answered 13/9, 2013 at 16:25 Comment(0)
T
21

Use sys.stdout.flush to force the header printed before the body:

import os
import sys

if __name__ == '__main__':
    with open(os.path.abspath('test.png'), 'rb') as f:
        print("Content-Type: image/png\n")
        sys.stdout.flush() # <---
        sys.stdout.buffer.write(f.read())

Or remove print, and use sys.stdout.buffer.write only:

import os
import sys

if __name__ == '__main__':
    with open(os.path.abspath('test.png'), 'rb') as f:
        sys.stdout.buffer.write(b"Content-Type: image/png\n\n") # <---
        sys.stdout.buffer.write(f.read())

NOTE

f.read() could cause a problem if the file is huge. To prevent that, use shutil.copyfileobj:

import os
import shutil
import sys

if __name__ == '__main__':
    with open(os.path.abspath('test.png'), 'rb') as f:
        sys.stdout.buffer.write(b"Content-Type: image/png\n\n")
        shutil.copyfileobj(f, sys.stdout.buffer)
Taxpayer answered 13/9, 2013 at 16:41 Comment(1)
Omg thank you for this. Cannot believe how hard it was to get this to workPul

© 2022 - 2024 — McMap. All rights reserved.