The Problem:
I have been playing around with CherryPy for the past couple of days but I’m still having some trouble with getting images to work how I could expect them to. I can save an uploaded image as a jpg without issue but I can’t convert it to a base64 image properly. Here’s the simple server I wrote:
server.py
#server.py import os import cherrypy #Import framework frameNumber = 1 lastFrame = '' lastFrameBase64 = '' class Root (object): def upload(self, myFile, username, password): global frameNumber global lastFrameBase64 global lastFrame size = 0 lastFrameBase64 = '' lastFrame = '' while True: data = myFile.file.read(8192) if not data: break size += len(data) lastFrame += data lastFrameBase64 += data.encode('base64').replace('n','') f = open('/Users/brian/Files/git-repos/learning-cherrypy/tmp_image/lastframe.jpg','w') f.write(lastFrame) f.close() f = open('/Users/brian/Files/git-repos/learning-cherrypy/tmp_image/lastframe.txt','w') f.write(lastFrameBase64) f.close() cherrypy.response.headers['Content-Type'] = 'application/json' print "Image received!" frameNumber = frameNumber + 1 out = "{"status":"%s"}" return out % ( "ok" ) upload.exposed = True cherrypy.config.update({'server.socket_host': '192.168.1.14', 'server.socket_port': 8080, }) if __name__ == '__main__': # CherryPy always starts with app.root when trying to map request URIs # to objects, so we need to mount a request handler root. A request # to '/' will be mapped to HelloWorld().index(). cherrypy.quickstart(Root())
When I view the lastframe.jpg
file, the image renders perfectly. However, when I take the text string found in lastframe.txt
and prepend the proper data-uri identifier data:image/jpeg;base64,
to the base64 string, I get a broken image icon in the webpage I’m trying to show the image in.
<!DOCTYPE> <html> <head> <title>Title</title> </head> <body> <img src="data:image/jpeg;base64,/9....." > </body> </html>
I have tried using another script to convert my already-saved jpg image into a data-uri and it works. I’m not sure what I’m doing wrong in the server example b/c this code gives me a string that works as a data-uri:
Working Conversion
jpgtxt = open('tmp_image/lastframe.jpg','rb').read().encode('base64').replace('n','') f = open("jpg1_b64.txt", "w") f.write(jpgtxt) f.close()
So basically it comes down to how is the data
variable taken from myFile.file.read(8192)
is different from the data
variable taken from open('tmp_image/lastframe.jpg','rb')
I read that the rb
mode in the open method tells python to read the file as a binary file rather than a string. Here’s where I got that.
Summary
In summary, I don’t know enough about python or the cherrypy framework to see how the actual data is stored when reading from the myFile
variable and how the data is store when reading from the output of the open()
method. Thanks for taking the time to look at this problem.
Advertisement
Answer
Base64 works by taking every 3 bytes of input and producing 4 characters. But what happens when the input isn’t a multiple of 3 bytes? There’s special processing for that, appending =
signs to the end. But that’s only supposed to happen at the end of the file, not in the middle. Since you’re reading 8192 bytes at a time and encoding them, and 8192 is not a multiple of 3, you’re generating corrupt output.
Try reading 8190 bytes instead, or read and encode the entire file at once.